D3.js d3js:使新的父数据下降到子节点

D3.js d3js:使新的父数据下降到子节点,d3.js,D3.js,我无法确定如何最好地将发生在父节点(例如SVGg元素)的数据更改传递给它的子节点(例如SVGcircle元素) 我读了很多书,但还是不明白 下面是一个最低限度的工作示例。本例假设您有一个名为svg的对象,它引用了包含svg元素的d3选择 data = [{"id":"A","name":"jim"},{"id":"B","name":"dave"},{"id":"C","name":"pete"}]; g = svg.selectAll("g").data(data, function(d)

我无法确定如何最好地将发生在父节点(例如SVG
g
元素)的数据更改传递给它的子节点(例如SVG
circle
元素)

我读了很多书,但还是不明白

下面是一个最低限度的工作示例。本例假设您有一个名为
svg
的对象,它引用了包含svg元素的d3选择

data = [{"id":"A","name":"jim"},{"id":"B","name":"dave"},{"id":"C","name":"pete"}];

g = svg.selectAll("g").data(data, function(d) { return d.id; }).enter().append("g");

g.append("circle")
      .attr("r", 3)
      .attr("cx", 100)
      .attr("cy", function(d,i) {return 100 + (i * 30);})

// The data gets passed down to the circles (I think):
console.log("circle data:");
d3.selectAll("g circle").each(function(d) { console.log(d.name); });     

// Now change the data, and update the groups' data accordingly
data = [{"id":"A","name":"carol"},{"id":"B","name":"diane"},{"id":"C","name":"susan"}];
svg.selectAll("g").data(data, function(d) { return d.id;});

// These are the results of the change:
console.log("after change, the group has:");
d3.selectAll("g").each(function(d) { console.log(d.name); });     
console.log("but the circles still have:");
d3.selectAll("g circle").each(function(d) { console.log(d.name); });   

有人能帮我找到一种简洁的方法,将新名称放入组的所有子元素中吗?在我的实际示例中,每个
g
都包含许多
圆圈
s。

有两种方法可以将数据从家长传播到孩子:

  • 我会暗中这样做。(
    selection.append
    selection.insert
    的实现实际上基于
    selection.select

  • 可以使用函数显式地重做数据联接,以接收父数据并将其返回到子数据的数组中

    svg.selectAll("g").selectAll("circle")
        .data(function(d) { return [d]; });
    
  • 这是一回事。第一个选项依赖于select中的一些特殊行为,因此一开始可能有点令人惊讶,但它很好,因为它使节点更新模式与通过insert/append创建节点的模式对称。如果需要在传播数据时对其应用任何更改,则第二个选项非常有用


    这里还有一篇您没有链接到的文章,可能也很有用:

    不确定您是否理解了它,但文档中肯定没有这篇文章。所有涉及元素分组的文档似乎都没有涉及子选择和对子元素的数据继承

    答案是使用构造来更新子元素,并将子元素附加到group enter()调用中

    让我知道这是否有效


    [此答案基于此coderwall帖子中的一些代码分组:

    谢谢您的评论。这些方法有效,但仅适用于每个
    g
    中的第一个元素。如果向每个组添加两个圆,则只有第一个选定的圆会用新数据更新。你知道为什么会这样吗?如果你有不止一个圆圈,你需要通过对它们进行不同的分类,然后选择(“circle.className”)(或者第二种方法的类似想法)来分别处理它们。如果在每个G中有很多个圈或者是一个变量,那么你可以考虑使用一个完整的数据连接和嵌套的数据来创建/删除圆圈。这看起来不是一个疯狂的用例,一个数据由一个以上的SVG元素表示。遗憾的是,没有API方法可以做到这一点!不,我同意这并不罕见。但是,考虑到您首先为每个圆显式地调用了一次append/insert来创建它们,因此为每个圆调用一次select对我来说似乎是一致的。我想是偏好的问题吧。我同意。我认为“多个SVG元素代表同一数据段”显然是一种边缘情况。不过,我认为当前更新第一个元素而不是其他元素的行为是“奇怪的”。谢谢你的帮助。
    svg.selectAll("g").selectAll("circle")
        .data(function(d) { return [d]; });
    
    data = [{"id":"A","name":"jim"},{"id":"B","name":"dave"},{"id":"C","name":"pete"}];
    
    function draw(data) {
      var g = svg.selectAll("g").data(data, function(d) { return d.id; })
    
      genter = g.enter().append("g");
    
      // This is the update of the circle elements - 
      // note that it is attached to the g data, not the enter()
      // This will update any circle elements that already exist
      g.each(function(d, i) {
        var group = d3.select(this);
        group.select("circle")
        .transition() 
          .attr("r", 3)
          .attr("cx", 100)
          .attr("cy", function(d,i) {return 100 + (i * 30);})
      }
    
      // The data DOES get passed down to the circles, and the enter() statement
      // will create a circle child for each data entry
      genter.append("circle")
          .attr("r", 3)
          .attr("cx", 100)
          .attr("cy", function(d,i) {return 100 + (i * 30);})
    }
    
    // Original drawing
    draw(data);
    
    // Now change the data, and update the groups' data accordingly
    data = [{"id":"A","name":"carol"},{"id":"B","name":"diane"},{"id":"C","name":"susan"}];
    
    // Second drawing, the SVG will be updated
    draw(data);