tl;博士
使用的目的selectAll(null)
是保证“输入”选择总是对应数据数组中的元素,数据中的每个元素都包含一个元素。
“进入”选择
为了回答您的问题,我们必须简要说明什么是 D3.js 中的“输入”选择。您可能知道,D3 的主要特性之一是将数据绑定到 DOM 元素的能力。
在 D3.js 中,当将数据绑定到 DOM 元素时,可能出现三种情况:
- 元素个数和数据点个数相同;
- 元素多于数据点;
- 数据点多于元素;
在第 3 种情况下,所有没有对应 DOM 元素的数据点都属于“输入”选择。
因此,在 D3.js 中,“输入”选择是在将元素连接到数据之后包含不匹配任何 DOM 元素的所有数据的选择。如果我们在“输入”选择中使用附加函数,D3 将创建新元素,为我们绑定该数据。
这是一个维恩图,解释了有关数据点数量/DOM 元素数量的可能情况:
将数据绑定到已经存在的 DOM 元素
让我们打破你提议的附加圈子的片段。
这...
var circles = svg.selectAll("circle")
.data(data)
... 将数据绑定到包含所有圆圈的选择。在 D3 术语中,这就是“更新”选择。
那么,这...
.enter()
.append("circle");
...表示“输入”选择,为每个与所选元素不匹配的数据点创建一个圆圈。
Sure, when there is no element (or a given class) in the selection, using that element (or that class) in the selectAll
method will work as intended. 因此,在您的代码段中,如果选择中没有<circle>
元素svg
,selectAll("circle")
则可用于为数据数组中的每个数据点附加一个圆圈。
这是一个简单的例子。有没有<p>
在<body>
和我们的“进入”选择将包含的数据阵列中的所有元素:
var body = d3.select("body");
var data = ["red", "blue", "green"];
var p = body.selectAll("p")
.data(data)
.enter()
.append("p")
.text(d=> "I am a " + d + " paragraph!")
.style("color", String)
<script src="https://d3js.org/d3.v4.min.js"></script>
但是如果我们在该页面中已经有一个段落会发生什么?我们来看一下:
var body = d3.select("body");
var data = ["red", "blue", "green"];
var p = body.selectAll("p")
.data(data)
.enter()
.append("p")
.text(d=> "I am a " + d + " paragraph!")
.style("color", String)
<script src="https://d3js.org/d3.v4.min.js"></script>
<p>Look Ma, I'm a paragraph!</p>
结果很明显:红色段落消失了!它在哪里?
第一个数据元素“red”绑定到已经存在的段落。然后,只创建了两个段落(我们的“输入”选择),蓝色的和绿色的。
发生这种情况是因为,当我们使用 时selectAll("p")
,我们选择了<p>
元素!<p>
该页面中已经有一个元素。
选择空
但是,如果我们使用selectAll(null)
,则不会选择任何内容!那个页面中已经有一个段落并不重要,我们的“输入”选择将始终包含数据数组中的所有元素。
让我们看看它的工作原理:
var body = d3.select("body");
var data = ["red", "blue", "green"];
var p = body.selectAll(null)
.data(data)
.enter()
.append("p")
.text(d=> "I am a " + d + " paragraph!")
.style("color", String)
<script src="https://d3js.org/d3.v4.min.js"></script>
<p>Look Ma, I'm a paragraph!</p>
这就是选择 null 的目的:我们保证所选元素和数据数组之间没有匹配项。
选择空值和性能
由于我们没有选择任何东西,这selectAll(null)
是迄今为止添加新元素的最快方法:我们不必遍历 DOM 搜索任何东西。
这是一个比较,使用jsPerf:
https://jsperf.com/selecting-null/1
在这个非常简单的场景中,selectAll(null)
速度要快得多。在充满DOM元素的真实页面中,差异可能更大。
何时不使用 selectAll(null)
正如我们刚刚解释的,selectAll(null)
不会匹配任何现有的 DOM 元素。对于始终附加数据数组中的所有元素的快速代码来说,这是一个很好的模式。
但是,如果您计划更新您的元素,也就是说,如果您计划进行“更新”(和“退出”)选择,请不要使用selectAll(null)
. 在这种情况下,选择您计划更新的元素(或类)。
因此,如果您想根据不断变化的数据数组更新圆圈,您可以执行以下操作:
//this is the "update" selection
var circles = svg.selectAll("circle")
.data(data);
//this is the "enter" selection
circles.enter()
.append("circle")
.attr("foo", ...
//this is the "exit" selection
circles.exit().remove();
//updating the elements
circles.attr("foo", ...
在这种情况下,如果您使用selectAll(null)
,圆圈将不断附加到选择中,堆积起来,并且不会删除或更新圆圈。
PS:作为历史的好奇,selectAll(null)
模式的创建可以追溯到 Mike Bostock 等人的这些评论:https : //github.com/d3/d3-selection/issues/79