如何用D3JS单个命令创建嵌套HTML?求返回父元素的API
解决D3.js链式调用中返回父元素的问题
你碰到的这个问题是D3.js链式调用的典型陷阱——每次调用append()后,返回的是新创建元素的选择集,而不是原来的父元素选择集,所以继续链式调用就会不断往当前元素里嵌套,导致结构完全不符合预期。
好在D3.js提供了几种方法帮你回到父元素,继续添加同级元素,同时保持代码的链式风格:
方法1:使用.parent()回退层级
.parent()方法会返回当前选择集的父元素选择集,让你可以一步步回到上一级,继续添加兄弟元素:
d3.select('body') .append('div') .attr('id', 'somediv') // 添加btn元素 .append('btn') .attr('id', 'somebutton') .html('Abc') // 回到somediv父元素 .parent() // 添加nested div .append('div') .attr('class', 'nested') // 添加第一个span .append('span') .attr('id', 'span1') .html('Blah blah blah') // 回到nested div父元素 .parent() // 添加第二个span .append('span') .attr('id', 'span2') .html('Bleh bleh bleh');
这种方法适合层级较少的场景,层级多的话回退步骤会显得繁琐。
方法2:使用.call()管理嵌套结构
.call()方法会把当前选择集作为参数传给回调函数,你可以在回调里直接操作父元素,同时保持外层的链式结构,代码可读性更强:
d3.select('body') .append('div') .attr('id', 'somediv') .call(somediv => { // 在somediv下添加btn somediv.append('btn') .attr('id', 'somebutton') .html('Abc'); // 在somediv下添加nested div,并继续处理它的子元素 somediv.append('div') .attr('class', 'nested') .call(nested => { nested.append('span').attr('id', 'span1').html('Blah blah blah'); nested.append('span').attr('id', 'span2').html('Bleh bleh bleh'); }); });
这种方式相当于把嵌套逻辑封装在回调里,不需要额外的临时变量,结构清晰。
方法3:使用.each()处理父元素
.each()方法会遍历当前选择集的每个元素,在回调里this指向当前父元素,你可以通过d3.select(this)来操作它:
d3.select('body') .append('div') .attr('id', 'somediv') .each(function() { // 给somediv添加btn d3.select(this) .append('btn') .attr('id', 'somebutton') .html('Abc'); // 给somediv添加nested div,并处理其子元素 d3.select(this) .append('div') .attr('class', 'nested') .each(function() { d3.select(this).append('span').attr('id', 'span1').html('Blah blah blah'); d3.select(this).append('span').attr('id', 'span2').html('Bleh bleh bleh'); }); });
这种方法和.call()效果类似,适合习惯用this操作DOM元素的场景。
为什么你的原始链式写法会出错?
简单来说,D3的append()方法的返回值是新创建的元素,而不是原来的父元素。比如你在append('btn')后链式调用append('div'),这个div会被添加到btn元素内部,而不是somediv内部,最终就形成了错误的嵌套结构。
内容的提问来源于stack exchange,提问作者Fabien Snauwaert
相关产品推荐
相关产品推荐

