【发布时间】:2012-11-23 14:00:58
【问题描述】:
有人能解释一下 D3.js 中 datum() 和 data() 的区别吗?我看到两者都在使用,但我不知道为什么你应该选择一个而不是另一个?
【问题讨论】:
标签: javascript d3.js
有人能解释一下 D3.js 中 datum() 和 data() 的区别吗?我看到两者都在使用,但我不知道为什么你应该选择一个而不是另一个?
【问题讨论】:
标签: javascript d3.js
我在这里从 Mike 自己那里找到了正确答案:
D3 - how to deal with JSON data structures?
如果要将数据绑定到单个 SVG 元素,请使用
(...).data([data])
或
(...).datum(data)
如果您想将数据绑定到多个 SVG 元素
(...).data(data).enter().append("svg")
.....
【讨论】:
enter() 绑定数据时,如果数据数组元素多于 SVG 元素,d3 会将其余数组元素与新创建的 SVG 元素绑定。
在对此进行了一些研究之后,我发现这里关于 SO 的答案并不完整,因为它们仅涵盖了当您使用输入 data 参数调用 selection.data 和 selection.datum 时的情况。即使在这种情况下,如果选择是单个元素与包含多个元素时,两者的行为也会有所不同。此外,这两种方法也可以在没有任何输入参数的情况下调用,以查询选择中的绑定数据/数据,在这种情况下,它们再次表现不同并返回不同的东西。
编辑 - 我对这个问题 here 发布了一个更详细的答案,但下面的帖子几乎涵盖了关于这两种方法的所有关键点以及它们之间的区别。
当提供data作为输入参数
selection.data(data) 将尝试在 data 数组的元素之间执行数据连接,选择会导致创建 enter()、exit() 和 update() 选择,您可以随后使用这些选择操作。这样做的最终结果是,如果您传入一个数组data = [1,2,3],则会尝试将每个单独的数据元素(即数据)与选择相连接。选择的每个元素将仅绑定一个 data 基准元素。
selection.datum(data) 完全绕过数据连接过程。这只是将整个data 分配给整个选择中的所有元素,而不像在数据连接的情况下那样将其拆分。因此,如果您想将整个数组 data = [1, 2, 3] 绑定到您的 selection 中的每个 DOM 元素,那么 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" 分别绑定到每个元素,selection.data() 将返回 ["a", "b", "c"]。重要的是要注意,如果selection 是单个元素(例如)绑定了基准"a",那么selection.data() 将返回["a"] 而不是"a",正如某些人所期望的那样。
selection.datum() 仅对单个选择有意义,因为它被定义为返回绑定到选择的第一个元素的数据。因此,在上面的示例中,选择由绑定数据为"a"、"b" 和"c" 的DOM 元素组成,selection.datum() 将简单地返回"a"。
请注意,即使
selection有一个元素,selection.datum()和selection.data()也会返回不同的值。前者返回选择的绑定数据(上例中的"a"),而后者返回数组中的绑定数据(上例中的["a"])。
希望这有助于澄清selection.data 和selection.datum() 在提供数据作为输入参数时以及在不提供任何输入参数的情况下查询绑定数据时的不同之处。
PS - 了解其工作原理的最佳方法是从 Chrome 中的空白 HTML 文档开始,然后打开控制台并尝试向文档中添加一些元素,然后使用 selection.data 和 @987654375 开始绑定数据@。有时,通过做事比阅读更容易“理解”某事。
【讨论】:
这里有一些不错的链接:
关于 D3“data()”的好讨论: Understanding how D3.js binds data to nodes
根据后者:
# selection.data([values[, key]])将指定的数据数组与当前选择连接起来。这 指定值是一个数据值数组,比如一个数组 数字或对象,或返回值数组的函数。
...
# selection.datum([value])获取或设置每个选定元素的绑定数据。不像 selection.data 方法,此方法不计算连接(因此 不计算进入和退出选择)。
【讨论】:
我认为HamsterHuey给出的解释是迄今为止最好的。
为了扩展它并给出差异的可视化表示,我创建了一个示例文档,至少说明了 data 和 datum 之间的部分差异。
以下答案更多是使用这些方法得出的意见,但如果我错了,我很高兴得到纠正。
这个例子可以在下面运行或者in this 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>
【讨论】: