Components 如何在D3中创建一个具有API的可重用组件,该API可以处理组件的每个实例?

Components 如何在D3中创建一个具有API的可重用组件,该API可以处理组件的每个实例?,components,d3.js,Components,D3.js,迈克有一篇关于D3写作的优秀文章。本文描述了如何使组件可配置的模式,以及如何将组件应用于选择 该模式通过将单个组件对象连接到数据,允许通过多种选择重用单个组件对象;e、 g var chart = myChart(); d3.select("div.chart") .data(data) .call(chart); 我的组件实现如下所示: function myChart() { function my(selection) { selection.each

迈克有一篇关于D3写作的优秀文章。本文描述了如何使组件可配置的模式,以及如何将组件应用于选择

该模式通过将单个组件对象连接到数据,允许通过多种选择重用单个组件对象;e、 g

var chart = myChart();

d3.select("div.chart")
  .data(data)
  .call(chart);
我的组件实现如下所示:

function myChart() {

    function my(selection) {
        selection.each(function(d, i) {

            // generate chart here
            // `d` is the data, `i` is the index, `this` is the element

            var state = false;

            var circle = d3.select(this).append("circle")
                .attr("r", "10")
                .style("fill", "#000")
                .on("click", toggleState);

            function toggleState() {
                // this function updates the current instance trapped by this closure
                (state = !state)
                    ? circle.style("fill", "#fff")
                    : circle.style("fill", "#000");
            }
        });
    }

    my.toggleState(i) {
        // How do I access the `i`th instance of the component here?
    }

    return my;
}
我想要实现的是允许调用方在给定其索引的情况下操作该组件的实例。例如,如果上面的选择器
div.chart
返回一个包含两个元素的选择,我想调用
chart.toggleState(1)
,让它更新选择中的第二个div


为了不让任何人混淆我为什么要这么做,调用方需要将两种类型的组件同步在一起。假设我有一个由圆表示的组件和另一个由矩形表示的组件。这两个组件必须是独立的,而不是相互绑定。我需要能够创建4个圆和4个矩形,当我单击一个矩形时,我希望能够根据索引顺序更新相应的圆。我已经知道如何从组件引发事件(d3.dispatch),并在事件中提供当前索引作为参数,但我还没有知道如何调用给定索引的组件的特定实例。

我认为最简单的方法是为每个组件提供相同的类。然后,当您调用
chart.toggleState(1)
时,您可以执行
d3.selectAll('.component').each(函数(d,i){if(i==index)doWhatever;})

特定选择的数据位于选择[0]中。可以选择第i个元素并设置属性:

var i = 3
d3.select(mySelection[0][i]).attr("fill","#fff")
如果你知道i,你可以这样写toggleState:

function toggleState(i) {
    var toggle_me = d3.select(circles[0][i])
    var state = !(toggle_me.attr("fill") == "#fff")
    toggle_me.attr("fill", (state ? "#fff": "#000"))
}
function toggleState() {
    var toggle_me = d3.select(this)
    var i = circles[0].findIndex(function(d) {
        return d == toggle_me[0][0]
    })
    if (i !== -1) {
        var state = !(toggle_me.attr("fill") == "#fff")
        toggle_me.attr("fill", (state ? "#fff": "#000"))
      // Do additional functions with i here
      //var triange = d3.select(triangles[0][i])
      //...
    }
}
如果你不知道i,你可以这样写toggleState:

function toggleState(i) {
    var toggle_me = d3.select(circles[0][i])
    var state = !(toggle_me.attr("fill") == "#fff")
    toggle_me.attr("fill", (state ? "#fff": "#000"))
}
function toggleState() {
    var toggle_me = d3.select(this)
    var i = circles[0].findIndex(function(d) {
        return d == toggle_me[0][0]
    })
    if (i !== -1) {
        var state = !(toggle_me.attr("fill") == "#fff")
        toggle_me.attr("fill", (state ? "#fff": "#000"))
      // Do additional functions with i here
      //var triange = d3.select(triangles[0][i])
      //...
    }
}
工作示例:

对于不同上下文中的相同答案,请参见:

我觉得这样做的方法似乎是只选择需要更新的元素,然后调用组件。其他任何东西都会违反您通过此组件模式实现的封装。我最近完成了一项基于Mikes文章的开源可重用组件的工作。非常感谢@timebandit!你的例子很贴切!希望它能在谷歌搜索“如何创建d3组件”时出现在搜索结果中