D3JS:在单个命令中附加简单的嵌套 HTML



考虑这个简单的HTML块:

<div id="somediv">
<btn id="somebutton">Abc</btn>
<div class="nested">
<span id="span1">Blah blah blah</span>
<span id="span2">Bleh bleh bleh</span>
</div>
</div>

我想使用 D3JS 动态重新创建它。

这适用于此目的:

(function() {
var somediv = d3.select( 'body' ).append( 'div' ).attr( 'id', 'somediv' );
somediv.append( 'btn' )
.attr( 'id', 'somebutton' )
.html( 'Abc' );
var nested = somediv.append( 'div' )
.attr( 'class', 'nested' );
nested.append( 'span' ).attr( 'id', 'span1' ).html( 'Blah blah blah' );
nested.append( 'span' ).attr( 'id', 'span2' ).html( 'Bleh bleh bleh' );
})();

。但是,它非常冗长,有四个命令和两个临时变量......最重要的是,它不容易阅读——不喜欢它。

我正在寻找一种在单个命令中做到这一点的方法。

到目前为止,我有这个:

d3.select( 'body' )
.append( 'div' )
.attr( 'id', 'somediv' )
.append( 'btn' )
.attr( 'id', 'somebutton' )
.html( 'Abc' )
.append( 'div' )
.attr( 'class', 'nested' )
.append( 'span' ).attr( 'id', 'span1' ).html( 'Blah blah blah' )
.append( 'span' ).attr( 'id', 'span2' ).html( 'Bleh bleh bleh' );

。但是(正如预期的那样(,这会导致不断嵌套(即:span2 在 span1 中,在div 中,在 BTN 中,在 #somediv 中。

D3 JS API 中是否有某些内容可以让我返回(到(父元素以继续从中追加?

您可以在单个命令中执行此操作,但它的可读性可能会低于您拥有的多个命令。例如:

d3.select("body")
.append("div")
.each(function() {
d3.select(this).append("button").html("button")
d3.select(this).append("div")
.each(function() {
d3.select(this).append("span").text("span")
d3.select(this).append("span").text("span")
})
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>

如您所见,这当然有效,但它可能不被视为可读,因为它是一种不寻常的 d3 模式,特别是当您需要与熟悉 d3 的其他人共享代码时。随着我们进一步嵌套,可读性也可能会降低。

您也可以使用我在上一个答案中建议的方法,并创建一个selection.appendSibling方法。通过创建方法的额外冗长,您可以获得看起来更干净的东西:

// modify d3.selection so that we can append a sibling
d3.selection.prototype.appendSibling = function(type) {
var siblings =  this.nodes().map(function(n) {
return n.parentNode.insertBefore(document.createElement(type), n.nextSibling);
})
return d3.selectAll(siblings).data(this.data());
}
d3.select("body")
.append("div")
.append("button").html("button")
.appendSibling("div") // append a sibling div to the button
.append("span").text("span1")
.appendSibling("span").text("span2"); // append a sibling span to the first span
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>

但我不建议这种方法 - 目前尚不清楚每行正在操纵什么选择。它旨在用于处理成对元素,例如dldd元素。


虽然上述两个有效,并且您也可以使用 enter 循环来附加两个跨度而不是独立的附加语句,但我仍然建议将代码分解为多个段,就像您最初使用的那样:

var container = d3.select("body").append("div");
var button = container.append("button").html("button");
var spans = container.append("div")
.selectAll(null)
.data([1,2])
.enter()
.append("span")
.text(function(d) { return "span"+ d; })
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>

(当然,我们可以跳过 span 和 button 的 var 声明,以使事情更紧密(

或者,更符合前两个片段:

var container = d3.select("body").append("div");
var button = container.append("button").html("button");
var div = container.append("div");
var span1 = div.append("span").text("span1")
var span2 = div.append("span").text("span2");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>

(同样,如果它被认为是噪音,我可以删除不必要的 var 声明(

最后两个片段实际上比第一个片段短。

虽然这偏离了问题,但我相信最后两个片段是有利的,原因如下:

  • 它允许轻松操作独立元素 - 假设您希望在按钮上安装事件侦听器,或者之后更改它
  • 很明显,正在
  • 操纵什么选择
  • 其他读者/同事更熟悉/更容易理解。

我更喜欢安德鲁·里德的回答,但你也可以使用this.parentNode将多个元素附加到同一个父元素。

我还使用长度为 2 的数组的enter()函数来创建具有动态 id 的span元素。更改数组长度以创建任意数量的元素。

var body = d3.select('body')
.append('div').attr('id', 'someDiv')
.append('button').attr('id', 'someBtn').html('Abc')
.each(function() {
d3.select(this.parentNode)
.append('div')
.selectAll('span').data([0, 1]).enter().append('span').attr('id', function(d, i) {
return 'span' + (i + 1)
}).html('Bleh Bleh Bleh')
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

最新更新