D3.js中为何g元素在enter阶段可操作,update阶段却失效?
D3.js通用更新模式中无法在enter阶段外设置g元素属性的问题解析
问题描述
解析XML字符串并使用D3.js构建树形图时,能在enter阶段成功创建完整树形结构,但尝试将元素配置移至update阶段时遇到问题:参照通用更新模式实现后,使用myGroups.select("g").attr("class", "x")无法为已创建的g元素设置class属性,但直接在enter阶段设置(myGroupsEnter.attr("class", "x"))却能生效。
代码示例
const svg = d3.select("#canvas"); const xmlAsText = ` <root attrib1="foo" att2="foO"> <child1 attrib1="ba">zwei</child1> <child2>eins</child2> </root>`; treeDataXml = (new DOMParser()).parseFromString(xmlAsText, "text/xml"); let hierarchy = d3.hierarchy(treeDataXml.children[0], d => d.children); nodesData = d3.tree()(hierarchy); var myGroups = svg.selectAll("g").data(nodesData.descendants()); myGroupsEnter = myGroups.enter().append("g") // 1) 可行 //myGroupsEnter .attr("class", "x"); // 2) 不可行 myGroups.select("g").attr("class", "x"); console.log(document.getElementById("canvas").childNodes[0].attributes[0]) alert(document.getElementById("canvas") .childNodes[0].attributes.length)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script> <svg id="canvas"></svg>
原因分析
- update集为空:首次运行代码时,SVG中没有任何g元素,
svg.selectAll("g").data(...)得到的myGroups(update阶段选择集)是空的,后续myGroups.select("g")自然找不到任何元素。 - enter集未合并:D3 v5中,enter阶段创建的元素默认不会自动合并到update选择集,
myGroups仅代表已有元素,无法访问enter阶段新创建的g元素。 - 选择器逻辑错误:
myGroups.select("g")是在myGroups的每个元素内部查找子g元素,但myGroups本身就是g元素的选择集,嵌套选择完全多余,反而会找不到目标元素。
解决方案
核心是合并enter和update选择集,让后续操作能覆盖所有新创建和已存在的元素,同时修正选择器逻辑:
修正后的代码
const svg = d3.select("#canvas"); const xmlAsText = ` <root attrib1="foo" att2="foO"> <child1 attrib1="ba">zwei</child1> <child2>eins</child2> </root>`; treeDataXml = (new DOMParser()).parseFromString(xmlAsText, "text/xml"); let hierarchy = d3.hierarchy(treeDataXml.children[0], d => d.children); nodesData = d3.tree()(hierarchy); var myGroups = svg.selectAll("g").data(nodesData.descendants()); // 处理enter阶段,创建新的g元素 const myGroupsEnter = myGroups.enter().append("g"); // 关键:将enter集合并到update集,使myGroups包含所有g元素 myGroups = myGroupsEnter.merge(myGroups); // 直接对合并后的选择集设置属性,作用于所有enter和update阶段的元素 myGroups.attr("class", "x"); console.log(document.getElementById("canvas").childNodes[0].attributes[0]) alert(document.getElementById("canvas").childNodes[0].attributes.length)
说明
merge()方法将enter阶段的新元素合并到update选择集,此时myGroups包含了所有与数据绑定的g元素(不管是新创建还是已存在)。- 直接对
myGroups调用attr(),不需要嵌套select("g"),因为myGroups本身就是针对g元素的选择集,操作会直接作用于每个g元素。
内容的提问来源于stack exchange,提问作者peer
相关产品推荐
相关产品推荐

