为什么我的数据选择器有副作用?
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 中,您可以看到它们之间的关系:
- 已加入群组 =
<g>
- joinedSegments =
<path>
- joinedIcons =
<circle>
这里是 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()
的副作用,我'我也提到了,因为黄色部分将消失。
潜在的问题是您有嵌套元素,并且每个元素(g
、path
和 circle
)都需要将数据绑定到它们,因为您根据数据重新设置属性。
您将数据显式绑定到 g
元素(第 122 行),因此工作正常。输入 selection 也一切正常,因为 .append()
具有 "inheriting" 绑定数据到附加元素的副作用。问题在于更新 selection。 .selectAll()
(第 143 和 144 行)您用于 select 嵌套元素并更新它们的属性 而不是 更新绑定数据,您依靠在函数中设置属性值。
有两种方法可以解决这个问题。第一个是您已经拥有的——select 那些嵌套元素显式更新它们的数据。然而,这需要额外的代码并且依赖于嵌套元素的顺序与其父元素相同,这是不能保证的。
更好的方法是简单地将第 143 和 144 行中的 .selectAll()
替换为 .select()
。这是可能的,因为在 g
下嵌套的每种类型只有一个元素,所以 selection 无论如何只包含一个元素。在这种情况下,.selectAll()
和 .select()
之间的主要区别是 .selectAll()
不会更新绑定到 selected 元素的数据,而 .select()
会。效果与您当前的解决方案相同——绑定到嵌套元素的数据已正确更新,除了您不需要任何额外代码并且不依赖于 selected 元素的顺序.
完成演示 here。
我正在使用 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 中,您可以看到它们之间的关系:
- 已加入群组 =
<g>
- joinedSegments =
<path>
- joinedIcons =
<circle>
这里是 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()
的副作用,我'我也提到了,因为黄色部分将消失。
潜在的问题是您有嵌套元素,并且每个元素(g
、path
和 circle
)都需要将数据绑定到它们,因为您根据数据重新设置属性。
您将数据显式绑定到 g
元素(第 122 行),因此工作正常。输入 selection 也一切正常,因为 .append()
具有 "inheriting" 绑定数据到附加元素的副作用。问题在于更新 selection。 .selectAll()
(第 143 和 144 行)您用于 select 嵌套元素并更新它们的属性 而不是 更新绑定数据,您依靠在函数中设置属性值。
有两种方法可以解决这个问题。第一个是您已经拥有的——select 那些嵌套元素显式更新它们的数据。然而,这需要额外的代码并且依赖于嵌套元素的顺序与其父元素相同,这是不能保证的。
更好的方法是简单地将第 143 和 144 行中的 .selectAll()
替换为 .select()
。这是可能的,因为在 g
下嵌套的每种类型只有一个元素,所以 selection 无论如何只包含一个元素。在这种情况下,.selectAll()
和 .select()
之间的主要区别是 .selectAll()
不会更新绑定到 selected 元素的数据,而 .select()
会。效果与您当前的解决方案相同——绑定到嵌套元素的数据已正确更新,除了您不需要任何额外代码并且不依赖于 selected 元素的顺序.
完成演示 here。