Answers:
我从迈克本人那里找到了正确答案:
如果要将数据绑定到单个SVG元素,请使用
(...).data([data])
要么
(...).datum(data)
如果要将数据绑定到多个SVG元素
(...).data(data).enter().append("svg")
.....
enter()
,如果数据数组元素比SVG元素更多,则d3会将其余的数组元素与新创建的SVG元素绑定。
经过一番研究之后,我发现SO上的答案还不完整,因为它们仅涵盖调用selection.data
和selection.datum
使用输入data
参数时的情况。即使在这种情况下,如果选择是单个元素,也包含多个元素,两者的行为就会有所不同。而且,这两种方法也可以在没有任何输入参数的情况下调用,以查询选择中的绑定数据/数据,在这种情况下,它们再次表现不同并返回不同的事物。
编辑-我在这里发布了对该问题的更详细的答案,但是下面的帖子捕获了有关这两种方法的所有关键点以及它们之间的区别。
提供 data
作为输入参数时
selection.data(data)
将尝试执行data
数组元素之间的数据联接enter()
,exit()
并进行update()
选择,从而创建,随后可以进行选择。这样做的最终结果是,如果您传入一个数组data = [1,2,3]
,则会尝试将每个单独的数据元素(例如,数据)与选择连接起来。选择的每个元素将只有一个data
绑定到它的基准元素。
selection.datum(data)
完全绕过数据联接过程。这只是将整个data
选择中的所有元素整体分配,而没有像数据联接那样将其拆分。因此,如果您要将整个数组绑定data = [1, 2, 3]
到中的每个DOM元素selection
,则selection.datum(data)
可以实现这一点。
警告:许多人认为这
selection.datum(data)
等同于,selection.data([data])
但这仅在selection
包含单个元素的情况下才是正确的 。如果selection
包含多个DOM元素,则将selection.datum(data)
整个绑定data
到选择中的每个单个元素。相反,selection.data([data])
仅将的整体绑定data
到中的第一个元素selection
。这与的数据连接行为一致selection.data
。
不提供data
输入参数时
selection.data()
将获取选择中每个元素的绑定基准,并将它们组合成一个返回的数组。因此,如果您selection
的数据包含3个DOM元素"a"
,"b"
并且"c"
分别绑定到每个DOM元素,则selection.data()
返回["a", "b", "c"]
。重要的是要注意,如果selection
是单个元素(例如)"a"
绑定了基准,则selection.data()
它将返回,["a"]
而不是"a"
某些预期的那样。
selection.datum()
仅对单个选择有意义,因为将其定义为返回绑定到选择的第一个元素的基准。因此,在上面的示例中,选择由具有"a"
,"b"
和的绑定基准的DOM元素组成"c"
,selection.datum()
将简单地返回"a"
。
注意,即使
selection
只有一个元素,selection.datum()
并selection.data()
返回不同的值。前者返回选择的绑定数据("a"
在上面的示例中),而后者返回数组内的绑定数据(["a"]
在上面的示例中)。
希望这有助于阐明如何selection.data
与selection.datum()
彼此不同既作为输入参数提供数据时和通过不提供任何输入参数查询结合的基准时。
PS-了解其工作原理的最佳方法是从Chrome中的空白HTML文档开始,打开控制台,尝试向该文档中添加一些元素,然后使用selection.data
和开始绑定数据selection.datum
。有时候,通过做事“读书”比读书更容易。
这里有一些很好的链接:
关于D3“ data()”的很好的讨论: 了解D3.js如何将数据绑定到节点
对于后者:
# selection.data([values[, key]])
将指定的数据数组与当前选择连接在一起。指定的值是数据值的数组,例如数字或对象的数组,或者是返回值的数组的函数。
...
# selection.datum([value])
获取或设置每个选定元素的绑定数据。与selection.data方法不同,此方法不计算联接(因此不计算输入和退出选择)。
我认为HamsterHuey给出的解释是迄今为止最好的。为了扩展它并给出差异的直观表示,我创建了一个示例文档,该文档至少说明了data
和之间的差异datum
。
以下答案更多是使用这些方法得出的意见,但是如果我错了,我很乐意得到纠正。
该示例可以在下面的Fiddle中运行。
const data = [1,2,3,4,5];
const el = d3.select('#root');
el
.append('div')
.classed('a', true)
.datum(data)
.text(d => `node => data: ${d}`);
const join= el
.selectAll('div.b')
.data(data);
join
.enter()
.append('div')
.classed('b', true)
.text((d, i) => `node-${i + 1} => data: ${d}`)
我认为这datum
很容易掌握,因为它不进行联接,但这当然也意味着它具有不同的用例。
对我来说,一个很大的差异-尽管还有更多差异-这data
是在d3图表上进行(实时)更新的自然方式,因为一旦获得,整个输入/更新/退出模式将使其变得简单。
datum
另一方面,在我看来,它更适合静态表示。例如,在下面的示例中,我可以在原始数组上循环并按索引访问数据来达到相同的结果,如下所示:
data.map((n, i) => {
el
.append('div')
.classed('a', true)
.datum(data)
.text(d => `node-${n} => data: ${d[i]}`);
});
在这里尝试:https : //jsfiddle.net/gleezer/e4m6j2d8/6/
再说一次,我认为这很容易掌握,因为您可以避免输入/更新/退出模式带来的精神负担,但是一旦您需要更新或更改选择,您肯定会更好地依靠.data()
。
const data = [1,2,3,4,5];
const el = d3.select('#root');
el
.append('div')
.classed('a', true)
.datum(data)
.text(d => `node => data: ${d}`);
const join= el
.selectAll('div.b')
.data(data);
join
.enter()
.append('div')
.classed('b', true)
.text((d, i) => `node-${i + 1} => data: ${d}`)
/* Ignore all the css */
html {
font-family: arial;
}
.l {
width: 20px;
height: 20px;
display: inline-block;
vertical-align: middle;
margin: 10px 0;
}
.l-a {
background: #cf58e4;
}
.l-b {
background: #42e4e4;
}
.a {
border-bottom: 2px solid #cf58e4;
}
.b {
border-bottom: 2px solid #42e4e4;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.6.0/d3.min.js"></script>
<div style="margin-bottom: 20px;">
<span class="l l-a"></span> .datum() <br />
<span class="l l-b"></span> .data()
</div>
<div id="root"></div>