有人可以解释 D3.js 中 datum() 和 data() 之间的区别吗?我看到两者都在使用,但我不确定您为什么应该选择其中一个?
4 回答
我从迈克本人那里找到了正确的答案:
如果要将数据绑定到单个 SVG 元素,请使用
(...).data([data])
或者
(...).datum(data)
如果要将数据绑定到多个 SVG 元素
(...).data(data).enter().append("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])
时才如此 。如果包含多个 DOM 元素,则将整个 绑定到选择中的每个元素。相反,仅将整个 绑定 到 中的第一个元素。这与 的数据连接行为一致。selection
selection
selection.datum(data)
data
selection.data([data])
data
selection
selection.data
不提供data
输入参数时
selection.data()
将为选择中的每个元素获取绑定数据并将它们组合成一个返回的数组。因此,如果您selection
在 data 中包含 3 个 DOM 元素"a"
,"b"
并"c"
分别绑定到每个元素,则selection.data()
返回["a", "b", "c"]
. 重要的是要注意,如果selection
是单个元素(例如)"a"
绑定到它的数据,那么selection.data()
将返回["a"]
而不是"a"
像某些人所期望的那样。selection.datum()
仅对单个选择有意义,因为它被定义为返回绑定到选择的第一个元素的数据。因此,在上面的示例中,选择由 DOM 元素组成,绑定数据为"a"
,"b"
和"c"
,selection.datum()
将简单地返回"a"
。
请注意,即使
selection
具有单个元素,selection.datum()
并且selection.data()
返回不同的值。前者返回选择的绑定数据("a"
在上面的示例中),而后者返回数组中的绑定数据(["a"]
在上面的示例中)。
希望这有助于阐明在提供数据作为输入参数时selection.data
以及selection.datum()
在通过不提供任何输入参数来查询绑定数据时两者之间的区别。
PS - 了解其工作原理的最佳方法是从 Chrome 中的空白 HTML 文档开始,然后打开控制台并尝试向文档中添加一些元素,然后使用selection.data
and开始绑定数据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>