使用 D3.js 为每个数据成员附加多个非嵌套元素
Posted
技术标签:
【中文标题】使用 D3.js 为每个数据成员附加多个非嵌套元素【英文标题】:Appending multiple non-nested elements for each data member with D3.js 【发布时间】:2014-02-24 11:55:11 【问题描述】:我想使用 d3 创建多个非嵌套元素来创建这样的结构:
<div id="parent">
<p> from data[0] </p>
<p> from data[0] </p>
<p> from data[1] </p>
<p> from data[1] </p>
<p> from data[2] </p>
<p> from data[2] </p>
</div>
创建嵌套结构类似于
d3.select('#parent').selectAll('p').data(data).enter().
append('p')...append('p')
但即使在追加之后我也想保持原来的选择,所以我可以继续追加到父元素。谢谢!
【问题讨论】:
【参考方案1】:的惯用方式是嵌套:
var divs = d3.select('#parent').selectAll('p').data(data).enter().append('div');
divs.append('p')
divs.append('p')
创建:
<div id="parent">
<div>
<p> from data[0] </p>
<p> from data[0] </p>
</div>
<div>
<p> from data[1] </p>
<p> from data[1] </p>
</div>
<div>
<p> from data[2] </p>
<p> from data[2] </p>
</div>
</div>
如果这不起作用,请保存您的选择并重复追加:
var enterSelection = d3.select('#parent').selectAll('p').data(data).enter();
enterSelection.append('p')
enterSelection.append('p')
然后对你添加的内容进行排序:
d3.select('#parent').selectAll('p').sort(function(a, b) return a.index - b.index; )
您需要将index
属性添加到描述排序顺序的data
的每个元素。正常的i
仅在特定选择的上下文中定义,当我们重新选择时会丢失。
【讨论】:
谢谢亚当。我没想到只是事后排序 完美运行,无论如何,我的数据将按数据排序。并且仅供任何人以后参考,我们必须在最后一行重新选择 d3.select('#parent') 进行排序,而不是使用 enterSelection,因为我们已经输入了()。谢谢! 您也可以使用 enterSelection.insert("p") 作为第二个元素。这应该消除了作为最后一步进行排序的需要。【参考方案2】:而不是.append()
,
您还可以在 .html()
中包装创建新内容的函数
d3.select('#parent')
.selectAll('div')
.data(data)
.enter()
.append('div')
.html(function(d) return "<p>" + from data[0] + "<p>" etc..... ;);
【讨论】:
【参考方案3】:第一项使用append()
,第二项使用insert()
。这消除了事后排序的任何需要(感谢@scuerda 指出这一点的评论)(JSFiddle):
data = [, , ];
var enterSelection = d3.select('#parent').selectAll('p').data(data).enter()
enterSelection.append('p').text(function(d, i) return 'from data[' + i + ']')
enterSelection.insert('p').text(function(d, i) return 'from data[' + i + ']')
这将为您提供所需的确切结构:
<p>from data[0]</p>
<p>from data[0]</p>
<p>from data[1]</p>
<p>from data[1]</p>
<p>from data[2]</p>
<p>from data[2]</p>
【讨论】:
这实际上是应该被接受的解决方案,因为它正是所要求的,没有任何弯路! 这很好,但你确定不是:<p>from data[0]</p> <p>from data[1]</p> <p>from data[2]</p> <p>from data[0]</p> <p>from data[1]</p> <p>from data[2]</p>
取决于 d3 版本。例如,对于 d3 v7,它不再工作了
仍在为较新版本的 d3 找到解决方案【参考方案4】:
您也可以在单个选择/输入循环中执行此操作,如下所示
d3.select('#parent').selectAll('p').data(data).enter().
append('p').text(function(d) return 'from data[0]')).
select(function() return this.parentNode; ).
append('p').text(function(d) return 'from data[0]'));
【讨论】:
这太恶心了,正是我要找的东西【参考方案5】:与上述类似,但成语不同。这更适用于嵌套选择方法,并且无需排序或插入:
var divs = d3.select('#parent');
var ps = divs.selectAll('#parent > div')
.data(d3.range(3)).enter().append('div');
ps.append('p').html(function(d,i) return 'from data[' + i + ']'; );
ps.append('p').html(function(d,i) return 'from data[' + i + ']'; );
更优雅(可能更快):
var divs = d3.select('#parent');
var ps = divs.selectAll('#parent > div')
.data(d3.range(3)).enter().append('div');
ps.append('p').html(function(d,i) return 'from data[' + i + ']'; )
.select(function() return this.parentNode.appendChild(this.cloneNode(true)); );
【讨论】:
【参考方案6】:1。复制每个元素
创建一个新列表,其中包含所有元素两次 (like this):
doubleData = data.reduce(function (res, current, index, array)
return res.concat([current, current]);
, []);
d3.select('#parent').selectAll('p').data(doubleData).enter().append('p')
2。输入两次
有时您想对附加的元素进行进一步的操作。在这种情况下,您可能想知道您是在访问第一个还是第二个添加的元素。为这些元素添加 first-paragraph
和 second-paragraph
类。
d3.select('#parent').selectAll('.first-paragraph')
.data(data)
.enter()
.append('p')
.attr('class', 'first-paragraph');
d3.select('#parent').selectAll('.second-paragraph')
.data(data)
.enter()
.append('p')
.attr('class', 'second-paragraph');
在这种情况下,您可能需要根据 Adam Pearce's answer 对元素进行排序。
【讨论】:
以上是关于使用 D3.js 为每个数据成员附加多个非嵌套元素的主要内容,如果未能解决你的问题,请参考以下文章