D3 的 selection.select 为什么会覆盖子节点的数据?
数据阿明
结论
这是 D3 selection.select 的数据传递行为造成的。对每个父节点调用 select 查找子节点时,D3 会把父节点绑定的数据传给子节点。如果子节点原本绑定了自己的数据,这次选择可能会覆盖它。
原因
问题中的每个 classA 节点都包含一个 classB 子节点。调用 classASelection.select('.classB') 后,每个父节点下匹配到的子节点都会继承父节点的数据:
classASelection.select('.classB');
所以,之后检查 classB 的数据时,看到的可能是对应 classA 的数据,而不是创建子节点时绑定的、包含 nickname 的数据。这也解释了为什么在创建元素时于 .each 回调中检查数据,结果符合预期;再次调用 select 后,数据却变了。
这种继承适用于父子节点对应同一条数据的场景,例如一个数据项对应一个容器和其中的标签。
保留子节点自己的数据
如果 classB 节点需要保留独立的数据,可以用 selectAll 选择:
const classBSelection = classASelection.selectAll('.classB');
selectAll 会创建子选择集,但不会像 select 那样把父节点的数据传给匹配的子节点。因此,这次选择本身不会覆盖子节点已有的数据。
如果调用 select 后还需要让子节点绑定独立数据,可以重新绑定:
classASelection
.select('.classB')
.datum(childData);
childData 应当是你希望绑定到所选子节点的数据。具体如何取得它,取决于原问题中的数据结构。
选择方式
- 每个父节点只选择一个子节点,且子节点应继承父节点数据:使用
select。 - 选择一个或多个子节点,并希望保留其独立数据:使用
selectAll。 - 调用
select后,若子节点需要独立数据:通过.datum(...)或适当的.data(...)重新绑定。
原问题提供的 jsfiddle 示例用于展示这一现象。仅凭描述无法确认示例中的具体数据结构,因此重新绑定时应根据实际结构提供 childData。
备注:内容仅供参考。
