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

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

问题描述

我已经看到一些带有这样的模式的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()
。附加( p);

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



然而, selectAll(null) 确实有效!这些元素会被附加。



那么, null 的含义是什么?我在这里缺少什么?






注意:这是一个自我回答的问题,



< h3>将数据绑定到已存在的DOM元素

让我们打破你建议的代码片段来附加圈子。



这个。 ..

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

...将数据绑定到包含所有圈子的选择。在D3 lingo中,这是更新选择。



然后,这......

  .enter()
.append(circle);

...表示输入选项,为每个数据点创建一个圆圈匹配一个选定的元素。



当然,当选择中没有元素(或给定的类)时,使用<$ c中的元素(或那个类) $ c> selectAll 方法将按预期工作。因此,在您的代码段中,如果 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 =>我是+ d +段落!)。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 =>我是+ d +段落!)。style(color,String) 

 < script src =https://d3js.org/d3.v4.min.js>< / script>< p>看看我,我是一个段落!< / p>  



结果是明确:红色段落消失了!在哪里?



第一个数据元素red绑定到现有的段落。然后,只创建了两个段落(我们的输入选择),蓝色和绿色。



这是因为,当我们使用 selectAll(p),我们选择了< p> 元素!该页面中已经有一个< p> 元素。



选择null



但是,如果我们使用 selectAll(null),将选择!在该页面中已经存在段落并不重要,我们的输入选择将始终包含数据数组中的所有元素。



让我们看看它是否有效:



  var body = d3.select(body); var data = [red,blue,green]; var p = body.selectAll(null).data(data).enter ()。append(p)。text(d =>我是+ d +段落!)。style(color,String) 

 < script src =https://d3js.org/d3.v4.min.js> < / script>< p>看看我,我是一个段落!< / p>  



这就是选择null的目的:我们保证所选元素与数据数组之间存在无匹配



选择null和性能



由于我们没有选择任何内容, selectAll(null)是到目前为止最快的追加方式新元素:我们不必遍历DOM搜索任何内容。



这是一个比较,使用 jsPerf



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



在这个非常简单的场景中, selectAll(null)大大加快了。在一个充满DOM元素的实际页面中,差异可能更大。



何时不使用selectAll(null)



正如我们刚刚解释的那样, selectAll(null)将不匹配任何现有的DOM元素。这是一个很好的模式,一个快速的代码总是附加数据数组中的所有元素。



但是,如果你打算更新你的元素,也就是说,如果您打算选择更新(和退出),使用 selectAll(null)。在这种情况下,选择您计划更新的元素(或类)。



因此,如果您想根据不断变化的数据数组更新圆圈,您会这样做类似这样的事情:

  //这是更新选择
var circles = svg.selectAll(circle )
.data(数据);

//这是输入选择
circles.enter()
.append(circle)
.attr(foo,.. 。

//这是退出选择
circles.exit()。remove();

//更新元素
圈子.attr(foo,...

在这种情况下,如果你使用 selectAll(null),圆圈将不断附加到选区,堆积,并且不会删除或更新任何圆圈。






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


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

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

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

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

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

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

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

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

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

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


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

The objective of using selectAll(null) is to guarantee that the "enter" selection always have all the elements present in the data array.


The "enter" selection

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.

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

  1. The number of elements and the number of data points are the same;
  2. There are more elements than data points;
  3. There are more data points than elements;

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

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.

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

Binding data to already existing DOM elements

Let's break your proposed snippet for appending circles.

This...

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

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

Then, this...

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

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

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.

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.

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

Selecting 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>

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

Selecting null and performance

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.

Here is a comparison, using jsPerf:

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

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

When NOT to use selectAll(null)

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.

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", ...

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: 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.js中'selectAll(null)'背后的原因是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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