为什么我的数据选择器有副作用?

Why does my Data selector have a side effect?

我正在使用 D3 构建一个小型可视化,需要将几个不同的东西绑定到同一个数据元素。我有一个观点,我似乎能够让它工作的唯一方法是对 Data() 进行多次调用,我觉得这可能效率低下。

例如,这是我的加入代码:

// Sort out the data joins
var joinedGroups = segments.selectAll(".menu").data(pie(data));
var joinedSegments = segments.selectAll(".menu-segment").data(pie(data));
var joinedIcons = segments.selectAll("circle").data(pie(data));

这里有一张图片可以帮助说明我正在构建的内容:

在我的 DOM 中,您可以看到它们之间的关系:

这里是 DOM 的输出来说明:

<g class="menu">
       <path class="menu-segment" d="" style="fill: rgb(255, 0, 0);"></path>
       <circle r="5" cx="29.999999999999996" cy="-51.96152422706632"></circle>  
</g>

然后我使用 joinedGroups 添加新的 <g><path><circle> 元素。那时我转而使用 joinedSegments 来更新我的饼图。这是我觉得不对的地方:

// Update existing segments and icons
joinedSegments.attr("d", arc);
joinedIcons.attr("cx", function(d) { return calcMidPoint(d).x; })
           .attr("cy", function(d) { return calcMidPoint(d).y; });

考虑之后,我得出结论,我现在应该能够使用我的 joinedGroups 和 select 所有 .menu-segment 个实例,来获得我所有的 <path>元素代替。

joinedGroups.selectAll(".menu-segment").attr("d", arc);

此时我以为我破解了,我不再使用joinedSegments所以我去掉了顶部的Data()调用,然后它停止正常工作,错过了一段那应该在那里。所以我对调用 Data() 的副作用感到有点困惑 - 谁能解释发生了什么?

您可以在此处查看示例 JSFiddle - 注释行 #123 和 #124 说明调用 Data() 的副作用,我'我也提到了,因为黄色部分将消失。

潜在的问题是您有嵌套元素,并且每个元素(gpathcircle)都需要将数据绑定到它们,因为您根据数据重新设置属性。

您将数据显式绑定到 g 元素(第 122 行),因此工作正常。输入 selection 也一切正常,因为 .append() 具有 "inheriting" 绑定数据到附加元素的副作用。问题在于更新 selection。 .selectAll()(第 143 和 144 行)您用于 select 嵌套元素并更新它们的属性 而不是 更新绑定数据,您依靠在函数中设置属性值。

有两种方法可以解决这个问题。第一个是您已经拥有的——select 那些嵌套元素显式更新它们的数据。然而,这需要额外的代码并且依赖于嵌套元素的顺序与其父元素相同,这是不能保证的。

更好的方法是简单地将第 143 和 144 行中的 .selectAll() 替换为 .select()。这是可能的,因为在 g 下嵌套的每种类型只有一个元素,所以 selection 无论如何只包含一个元素。在这种情况下,.selectAll().select() 之间的主要区别是 .selectAll() 不会更新绑定到 selected 元素的数据,而 .select() 会。效果与您当前的解决方案相同——绑定到嵌套元素的数据已正确更新,除了您不需要任何额外代码并且不依赖于 selected 元素的顺序.

完成演示 here