在 d3.js 中不使用 selectAll 时会忽略第一个数据

Aya*_*yan 1 html javascript jquery d3.js

片段1

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>D3 Test</title>
  <script type="text/javascript" src="http://d3js.org/d3.v3.js"></script>
</head>

<body>
  <script type="text/javascript">
    var data = [];
    for (i = 0; i < 3; i += 1) {
      data.push(i);
    }
    d3.select('body')
      // .selectAll('p')
      .data(data)
      .enter()
      .append('p')
      .text(function(d) {
        return d;
      });
  </script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

片段2

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>D3 Test</title>
  <script type="text/javascript" src="http://d3js.org/d3.v3.js"></script>
</head>

<body>
  <script type="text/javascript">
    var data = [];
    for (i = 0; i < 3; i += 1) {
      data.push(i);
    }
    d3.select('body')
      .selectAll('p')
      .data(data)
      .enter()
      .append('p')
      .text(function(d) {
        return d;
      });
  </script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

该代码.selectAll('p')在第一个片段中被注释。我不明白为什么第一个数据会因为这个原因而被忽略。

p我是 d3.js 的新手,我的理解是,因为我的 HTML 中没有标签,所以.selectAll('p')返回一个空选择并p根据我的数据计数进行附加。

提前致谢。

Ger*_*ado 5

要了解发生了什么,您必须了解什么是“输入”选择。本文底部的示例简要解释了“输入”选择以及为什么必须在第二个片段中执行此操作,即使文档中selectAll("p")没有任何内容。<p>

现在,让我们看看您的片段:

你的第一个片段

在你的第一个片段中,data[0, 1, 2]. 所以,你有 3 个元素。当你选择body时,bodyDOM中显然有一个。因此,您将第一个数据 (0) 与该元素(主体)相关联。现在,您有 2 个未与任何 DOM 元素关联的数据:12。这两个数据是您的“输入”选择。当您附加 时<p>,您的输入选择只有这两个数字。

你的第二个片段

在你的第二个片段中,数据又是[0, 1, 2]. 不同之处在于,现在您选择了所有<p>...但没有。这是我链接的示例中的“占位符”。<p>由于DOM 中没有与数据关联的内容,因此您的输入选择包含所有 3 个数据: 012

正如我在示例中所写:

如果在“输入”选择中选择了不存在的内容,则“输入”选择将始终包含您的所有数据。


占位符在“输入”选择中的作用

什么是输入选择?

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

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

在情况#3中,所有没有对应DOM元素的数据点都属于回车选择。因此,在 D3.js 中,输入选择是在将元素连接到数据后包含与任何 DOM 元素不匹配的所有数据的选择。如果我们append输入选择中使用函数,D3 将为我们创建绑定该数据的新元素。

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

在此输入图像描述

正如我们所看到的,输入选择是左侧的蓝色区域:没有对应 DOM 元素的数据点。

输入选择的结构

通常,输入选择有以下 4 个步骤:

  1. selectAll:选择DOM中的元素;
  2. data:对数据进行统计和解析;
  3. enter:将选择与数据进行比较,创建新元素;
  4. append:追加 DOM 中的实际元素;

这是一个非常基本的示例(查看 中的 4 个步骤var divs):

var data = [40, 80, 150, 160, 230, 260];

var body = d3.select("body");

var divs = body.selectAll("div")
    .data(data)
    .enter()
    .append("div");

divs.style("width", function(d) { return d + "px"; })
    .attr("class", "divchart")
    .text(function(d) { return d; });
Run Code Online (Sandbox Code Playgroud)

这就是结果(这里是 jsfiddle):

在此输入图像描述

请注意,在本例中,我们用作selectAll("div")“输入”选择变量中的第一行。我们有一个包含 6 个值的数据集,D3 为我们创建了 6 个 div。

占位符的作用

但是假设我们的文档中已经有一个 div,例如<div>This is my chart</div>顶部的内容。在这种情况下,当我们写:

body.selectAll("div")
Run Code Online (Sandbox Code Playgroud)

我们正在选择现有的 div。因此,我们的输入选择将只有 5 个没有匹配元素的数据。例如,在这个 jsfiddle 中,HTML 中已经有一个 div(“这是我的图表”),结果将是:

在此输入图像描述

我们不再看到值“40”:我们的第一个“栏”消失了,原因是我们的“输入”选择现在只有 5 个元素。

这里我们必须理解的是,在我们的输入选择变量的第一行中,selectAll("div")那些 div 只是占位符。我们不必选择所有的divsif 我们要追加divs,或者所有的circleif 我们要追加circle。我们可以选择不同的东西。而且,如果我们不打算进行“更新”或“退出”选择,我们可以选择任何内容

var divs = body.selectAll(".foo")//this class doesn't exist, and never will!
    .data(data)
    .enter()
    .append("div");
Run Code Online (Sandbox Code Playgroud)

通过这种方式,我们选择了所有“.foo”。在这里,“foo”是一个不仅不存在的类,而且也从未在代码中的其他任何地方创建过!不过没关系,这只是一个占位符。逻辑是这样的:

如果在“输入”选择中选择了不存在的内容,则“输入”选择将始终包含您的所有数据。

现在,选择.foo,我们的“输入”选择有 6 个元素,即使我们在文档中已经有一个 div:

在此输入图像描述

这是相应的 jsfiddle

选择null

到目前为止,保证您不选择任何内容的最佳方法是选择null。不仅如此,这种替代方案比其他任何方案都要快得多。

因此,对于输入选择,只需执行以下操作:

selection.selectAll(null)
    .data(data)
    .enter()
    .append(element);
Run Code Online (Sandbox Code Playgroud)

这是一个演示小提琴:https ://jsfiddle.net/gerardofurtado/th6s160p/

结论

在处理“输入”选择时,请格外小心,不要选择已经存在的内容。您可以使用 中的任何内容selectAll,甚至是不存在且永远不会存在的内容(如果您不打算进行“更新”或“退出”选择)。

示例中的代码基于 Mike Bostock 的代码:https ://bl.ocks.org/mbostock/7322386