选择null:D3中的selectAll(null)背后的原因是什么? [英] Selecting null: what is the reason behind selectAll(null) in D3?

查看:109
本文介绍了选择null:D3中的selectAll(null)背后的原因是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经看到了一些D3代码,它们具有类似这样的模式来添加元素:

I've seen some D3 codes with a pattern like this for appending elements:

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

我真的没有这个片段.为什么选择null?

I really don't get this snippet. Why selecting null?

我对D3的理解方式是,如果要添加圆,应该是:

The way I understand D3, if one is appending circles, it should be:

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

以同样的方式,如果要附加HTML段落,则应为:

The same way, if one is appending HTML paragraphs it should be:

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

对于类也是如此:如果将元素添加为类foo,则应为selectAll(".foo").

The same goes for classes: if one is appending elements with a class foo, it should be selectAll(".foo").

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

However, selectAll(null) does work! The elements get appended.

那么,null是什么意思?我在这里想念什么?

So, what's the meaning of that null? What am I missing here?

注意:这是一个自我解答的问题,试图在以前的许多问题已经涉及到该主题,而API没有对此进行解释.以下大多数答案来自我在已绝版的 StackOverflow文档中编写的示例.

Note: this is a self-answered question, trying to provide a "canonical" Q&A on a subject that has been touched on by many previous questions and not explained by the API. Most of the answer below is from an example I wrote in the extinct StackOverflow Documentation.

推荐答案

tl; dr

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

tl;dr

The objective of using selectAll(null) is to guarantee that the "enter" selection always corresponds to the elements in the data array, containing one element for every element in the data.

要回答您的问题,我们必须简要解释一下D3.js中的输入"选择.您可能知道,D3的主要功能之一是绑定数据的功能到DOM元素.

To answer your question, we have to briefly explain what is an "enter" selection in D3.js. As you probably know, one of the main features of D3 is the ability of binding data to DOM elements.

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

In D3.js, when one binds data to DOM elements, three situations are possible:

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

在情况#3中,所有没有相应DOM元素的数据点都属于输入"选择.

In the situation #3, all the data points without a corresponding DOM element belong to the "enter" selection.

因此,在D3.js中,输入"选择是将元素连接到数据后包含与任何DOM元素都不匹配的所有数据的选择.如果我们在输入"选项中使用附加函数,则D3将创建新元素,并为我们绑定该数据.

Thus, In D3.js, "enter" selections are selections that, after joining elements to the data, contains all the data that don't match any DOM element. If we use an append function in an "enter" selection, D3 will create new elements, binding that data for us.

这是一个维恩图,解释了有关数据点数量/DOM元素数量的可能情况:

This is a Venn diagram explaining the possible situations regarding number of data points/number of DOM elements:

让我们打破您建议的添加圈子的代码段.

Let's break your proposed snippet for appending circles.

这个...

var circles = svg.selectAll("circle")
    .data(data)

...将数据绑定到包含所有圆的选区.在D3术语中,这是更新"选择.

... binds the data to a selection containing all circles. In D3 lingo, that's the "update" selection.

然后,这个...

.enter()
.append("circle");

...代表输入"选择,为与所选元素不匹配的每个数据点创建一个圆圈.

... represents the "enter" selection, creating a circle for each data point that doesn't match a selected element.

当然,当在所述选择没有元件(或一个给定类),使用在方法,其元素(或类)如预期将工作.因此,在您的代码段中,如果svg选择中没有<circle>元素,则可以使用selectAll("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. So, in your snippet, if there is no <circle> element in the svg selection, selectAll("circle") can be used to append a circle for each data point in the data array.

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

Here is a simple example. There is no <p> in the <body>, and our "enter" selection will contain all the elements in the data array:

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>

但是,如果我们在该页面上已经有一个段落,会发生什么?让我们看一下:

But what happens if we already have a paragraph in that page? Let's have a look:

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>

结果很明显:红色的段落消失了!在哪里?

The result is clear: the red paragraph disappeared! Where is it?

第一个数据元素红色"绑定到已经存在的段落.然后,仅创建了两个段落(我们的输入"选择),蓝色和绿色.

The first data element, "red", was bound to the already existing paragraph. Then, just two paragraphs were created (our "enter" selection), the blue one and the green one.

发生这种情况是因为,当我们使用selectAll("p")时,我们选择了<p>个元素!在该页面中已经有一个<p>元素.

That happened because, when we used selectAll("p"), we selected, well, <p> elements! And there was already one <p> element in that page.

但是,如果我们使用selectAll(null),则不会选择什么!不管该页面中是否已有一个段落,我们的输入"选择将始终具有数据数组中的所有元素.

However, if we use selectAll(null), nothing will be selected! It doesn't matter that there is already a paragraph in that page, our "enter" selection will always have all the elements in the data array.

让我们看看它是否有效:

Let's see it working:

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的目的:我们保证所选元素和数据数组之间没有不匹配.

And that's the purpose of selecting null: we guarantee that there is no match between the selected elements and the data array.

由于我们没有选择任何内容,因此selectAll(null)是迄今为止添加新元素的最快方法:我们不必遍历DOM搜索任何内容.

Since we are not selecting anything, selectAll(null) is by far the fastest way to append new elements: we don't have to traverse the DOM searching for anything.

这是一个比较,使用 jsPerf :

https://jsperf.com/selecting-null/1

在这种非常简单的情况下,selectAll(null)的速度要快得多.在一个充满DOM元素的真实页面中,差异可能更大.

In this very simple scenario, selectAll(null) was substantially faster. In a real page, full of DOM elements, the difference may be even bigger.

正如我们刚才所解释的,selectAll(null)与任何现有的DOM元素都不匹配.对于总是将数据数组中的所有元素都附加到后面的快速代码,这是一个很好的模式.

As we just explained, selectAll(null) won't match any existing DOM element. It's a nice pattern for a fast code that always append all the elements in the data array.

但是,如果您打算更新您的元素,也就是说,如果您打算进行更新"(和退出")选择,则不要使用selectAll(null).在这种情况下,请选择您打算更新的元素(或类).

However, if you plan to update your elements, that is, if you plan to have an "update" (and an "exit") selection, do not use selectAll(null). In that case, select the element (or the class) you plan to update.

因此,如果要根据变化的数据数组更新圆,则可以执行以下操作:

So, if you want to update circles according to a changing data array, you would do something like this:

//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),则圆圈将不断添加到所选内容中,进行堆积,并且不会删除或更新任何圆圈.

In that case, if you use selectAll(null), the circles will be constantly appended to the selection, piling up, and no circle will be removed or updated.

PS:正如历史上的好奇心一样,selectAll(null)模式的创建可以追溯到Mike Bostock和其他人的以下评论: https://github.com/d3/d3-selection/issues/79

PS: Just as a historical curiosity, the creation of the selectAll(null) pattern can be traced back to these comments by Mike Bostock and others: https://github.com/d3/d3-selection/issues/79

这篇关于选择null:D3中的selectAll(null)背后的原因是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆