繁体   English   中英

选择 null:D3 中 selectAll(null) 背后的原因是什么?

[英]Selecting null: what is the reason behind selectAll(null) in D3?

我已经看到一些 D3 代码带有这样的模式来附加元素:

var circles = svg.selectAll(null)
    .data(data)
    .enter()
    .append("circle");

我真的不明白这个片段。 为什么选择null

我理解 D3 的方式,如果一个是附加圆圈,它应该是:

var circles = svg.selectAll("circle")
    .data(data)
    .enter()
    .append("circle");

同样,如果要附加 HTML 段落,它应该是:

var circles = svg.selectAll("p")
    .data(data)
    .enter()
    .append("p");

类也是如此:如果一个类附加元素foo ,它应该是selectAll(".foo")

但是, selectAll(null)确实有效! 元素被追加。

那么,那个null什么意思? 我在这里缺少什么?


注意:这是一个自我回答的问题,试图针对许多以前的问题涉及但 API 未解释的主题提供“规范”问答 下面的大部分答案来自我在已灭绝的StackOverflow 文档中写的一个例子。

tl;博士

使用selectAll(null)是保证“输入”选择始终对应于数据数组中的元素,数据中的每个元素都包含一个元素。


“进入”选择

为了回答您的问题,我们必须简要解释一下 D3.js 中什么是“输入”选择。 您可能知道,D3 的主要特性之一是将数据绑定到 DOM 元素的能力。

在 D3.js 中,当将数据绑定到 DOM 元素时,可能出现三种情况:

  1. 元素个数和数据点个数相同;
  2. 元素多于数据点;
  3. 数据点多于元素;

在情况 #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. 因此,在您的代码段中,如果svg选择中没有<circle>元素,则可以使用selectAll("circle")为数据数组中的每个数据点附加一个圆圈。

这是一个简单的例子。 <body>没有<p> ,我们的“输入”选择将包含数据数组中的所有元素:

 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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM