事件处理程序中的d3选择:select vs selectAll

时间:2014-12-11 13:47:29

标签: javascript d3.js css-selectors

在使用D3.js的onclick-handler中按类选择SVG元素时,我正在观察(对我而言)意外行为。我将介绍一个简单的工作示例,然后是两个JS小提琴来说明问题。

该示例包含两个<rect>。它们的行为应该像单选按钮一样:单击时,矩形是专门选择的。如果任何其他矩形已处于活动状态,则会将其停用。任何活动矩形都存储在变量currently_active中。

testdata = [
  {
    unique_id: "3-2-1",
    y: 10
  },
  {
    unique_id: "2-3-1",
    y: 40
  }
];

var svg = d3.select("body").append("svg");
var rects = svg.selectAll(".node").data(testdata);

var currently_active = null;

rects.enter().append("rect")
.attr("x",10)
.attr("y", function(d){return d.y;})
.attr("width", 60)
.attr("height", 20)
.attr("class", function(d){return "node node-"+d.unique_id;})
.on("click", function(d){
    var node_id = d.unique_id;
    console.log(node_id);
    if (currently_active == node_id) return;
    if (currently_active) {
        var thenode = svg.select(".node-"+currently_active);
        thenode.style("fill", null);
        currently_active = null;
    }
    var thenode = svg.select(".node-"+node_id);
    thenode.style("fill", "#d11");

    currently_active = node_id;
});     

这个简单的版本按预期工作。注意:单击的node_id将登录到JS控制台。

  1. 未按预期工作:http://jsfiddle.net/bdy8z7dh/1/
    • 在这里,我将代码移动到最小的“可重用图表”功能中。
    • 此版本无法正常工作:它在第一次单击事件时有效,但在下一个事件中未定义node_id(请参阅JS控制台)。
    • 请注意,我在变量中保存了“parent”元素,以便在事件回调中不使用全局d3.select
  2. 再次按预期工作:http://jsfiddle.net/bdy8z7dh/2/
    • 我只是将parent.select更改为parent.selectAll,现在该示例再次按预期工作。
  3. 另一个解决方法是使用全局d3.select代替parent.select。但有一些我不明白的东西:为什么第二小提琴不起作用?我错过了什么?它可能与JS中的作用域有关,因为第一个最小的例子有效吗?

    PS。我的目的是将所有三个代码片段放在小提琴中,但我的代表太低而不能发布几个链接。我还想链接到Mike Bostock关于可重用图表的文章,但是现在你可以通过Google搜索“寻找可重复使用的图表”来找到它

1 个答案:

答案 0 :(得分:0)

@LarsKotthoff的评论完全正确。

您正在使用正确的数据初始化元素,因此第一个开关运行良好。但是,只要您从第一个选择的按钮切换,parent.select调用就会将所选按钮上的数据更改为最初仅在父项上的整个数据数组。但是,parent.selectAll不会传播父数据,因此在这种情况下你没事。

为验证这一点,我在您的示例中的第19行添加了另一个控制台输出:

相关文档部分位于https://github.com/mbostock/d3/wiki/Selections#select,其中显示的是selection.select()

  

如果当前元素具有关联数据,则此数据将继承   返回的子选择,并自动绑定到新的   选定的元素。

和约selection.selectAll()

  

子选择不会从当前选择继承数据;   但是,如果将数据值指定为函数,则此函数   将使用祖先节点和组的数据d调用   index i确定子选择的数据绑定。

编辑:

您在下面的评论中是正确的:您的第一个示例有效,因为您将数据直接分配给rect元素,因此svg元素没有要传播的数据。 selection.select()仅在父项具有数据时才覆盖子选择上的数据。