javascript - 选择空 : what is the reason behind selectAll(null) in D3?

标签 javascript d3.js

我已经看到一些 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 是什么意思? ?我在这里缺少什么?

注意:这是一个自我回答的问题,trying to provide a "canonical" Q&A on a subject that has been touched on by many previous questions并且没有由 API 解释。下面的大部分答案来自我在灭绝StackOverflow Documentation中写的一个例子。 .

最佳答案

tl;博士

使用目的selectAll(null)就是保证“回车”选择总是 对应于数据数组中的元素,对于数据中的每个元素包含一个元素。

“输入”选择

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

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

  • 元素个数和数据点个数相同;
  • 元素多于数据点;
  • 数据点多于元素;

  • 在情况 #3 中,所有没有对应 DOM 元素的数据点都属于“输入”选择。

    因此,在 D3.js 中,“输入”选择是在将元素连接到数据之后包含不匹配任何 DOM 元素的所有数据的选择。如果我们在“输入”选择中使用 append 函数,D3 将创建新元素,为我们绑定(bind)该数据。

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

    enter image description here

    将数据绑定(bind)到已经存在的 DOM 元素

    让我们打破你提议的附加圈子的片段。

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

    ... 将数据绑定(bind)到包含所有圆圈的选择。在 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方法将按预期工作。因此,在您的代码段中,如果没有 <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”绑定(bind)到已经存在的段落。然后,只创建了两个段落(我们的“输入”选择),蓝色的和绿色的。

    这是因为,当我们使用 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

    关于javascript - 选择空 : what is the reason behind selectAll(null) in D3?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46147231/

    相关文章:

    javascript - 使用按钮javascript更改某些东西的颜色

    javascript - 如何在鼠标悬停在另一个 div 上时隐藏 div?

    javascript - D3js 世界地图和 svg 组元素宽度出现问题

    javascript - 无法在 grails 应用程序中使用 selenium 检索文本框文本

    javascript - 交互式世界地图

    javascript - 元素不会 appendChild

    javascript - 汇总 js : how to make a closure like d3?

    javascript - 如何将 D3 中的连接路径/线移动到形状的边缘

    javascript - D3 - 来自同一源文件的多个词云

    javascript - d3.scaleSequential "interpolator is not a function"