D3.js - 数据连接

数据连接是 D3.js 中的另一个重要概念。它与选择一起使用,使我们能够根据我们的数据集(一系列数值)操作 HTML 文档。默认情况下,D3.js 在其方法中为数据集提供最高优先级,并且数据集中的每个项对应于 HTML 元素。本章详细介绍了数据连接。

什么是数据连接?

数据连接使我们能够根据现有 HTML 文档中的数据集注入,修改和删除元素(HTML 元素以及嵌入的 SVG 元素)。默认情况下,数据集中的每个数据项对应于文档中的元素(图形)。

随着数据集的变化,也可以轻松地操作相应的元素。数据连接在我们的数据和文档的图形元素之间创建了一种紧密的关系。数据连接使得基于数据集的元素操作变得非常简单和容易。

数据连接如何工作?

数据连接的主要目的是使用给定的数据集映射现有文档的元素。它根据给定的数据集创建文档的虚拟表示,并提供使用虚拟表示的方法。让我们考虑一个简单的数据集,如下所示。

[10, 20, 30, 25, 15]

数据集有五个项目,因此可以映射到文档的五个元素。让我们使用选择器的 selectAll() 方法和数据连接的 data() 方法将它映射到以下文档的 li 元素。

HTML

<ul id = "list">
   <li><li>
   <li></li>
</ul> 

D3.js 代码

d3.select("#list").selectAll("li").data([10, 20, 30, 25, 15]);

现在,文档中有五个虚拟元素。前两个虚拟元素是文档中定义的两个 li 元素,如下所示。

1. li - 10
2. li - 20

对于前两个 li,我们可以使用所有选择器的元素修改方法,如 attr()style()text() 等,如下所示。 ****

d3.select("#list").selectAll("li")
   .data([10, 20, 30, 25, 15])
   .text(function(d) { return d; });

text() 方法中的函数用于获取 li 元素映射数据。这里,d 表示第一个 li 元素为 10,第二个 li 元素为 20。

接下来的三个元素可以映射到任何元素,可以使用数据连接的 enter() 和 selector 的 append() 方法完成。 enter() 方法提供对剩余数据的访问(未映射到现有元素),append() 方法用于从相应数据创建新元素。让我们为剩余的数据项创建 li。数据如下 -

3. li - 30
4. li - 25
5. li - 15

创建新的 li 元素的代码如下 -

d3.select("#list").selectAll("li")
   .data([10, 20, 30, 25, 15])
   .text(function(d) { return "This is pre-existing element and the value is " + d; })
   .enter()
   .append("li")
   .text(function(d) 
      { return "This is dynamically created element and the value is " + d; });

数据连接提供了另一种称为 exit() 的方法,用于处理从数据集中动态删除的数据项,如下所示。

d3.selectAll("li")
   .data([10, 20, 30, 15])
   .exit()
   .remove()

在这里,我们使用 exit()remove() 方法从数据集及其对应的 li 中删除了第四个项目。

完整的代码如下 -

<!DOCTYPE html>
<html>
   <head>
      <script type = "text/javascript" src = "https://d3js.org/d3.v4.min.js"></script>
      <style>
         body { font-family: Arial; }
      </style>
   </head>

   <body>
      <ul id = "list">
         <li></li>
         <li></li>
      </ul>
        
      <input type = "button" name = "remove" value = "Remove fourth value" 
         onclick = "javascript:remove()" />
      
      <script>
         d3.select("#list").selectAll("li")
            .data([10, 20, 30, 25, 15])
            .text(function(d) 
               { return "This is pre-existing element and the value is " + d; })
            .enter()
            .append("li")
            .text(function(d) 
               { return "This is dynamically created element and the value is " + d; });
             
         function remove() {
            d3.selectAll("li")
            .data([10, 20, 30, 15])
            .exit()
            .remove()
         }
      </script>
   </body>
</html>

上述代码的结果如下 -

  • This is pre-existing element and the value is 10
  • This is pre-existing element and the value is 20
  • This is dynamically created element and the value is 30
  • This is dynamically created element and the value is 25
  • This is dynamically created element and the value is 15

数据连接方法

数据连接提供以下四种方法来处理数据集 -

  • datum()
  • data()
  • enter()
  • exit()

让我们详细介绍这些方法。

datum() 方法

datum() 方法用于为 HTML 文档中的单个元素设置值。一旦使用选择器选择元素,就会使用它。例如,我们可以使用 select() 方法选择现有元素(p 标签),然后使用 datum() 方法设置数据。设置数据后,我们可以更改所选元素的文本或添加新元素,并使用 datum() 方法设置的数据分配文本。

创建页面“datajoin_datum.html”并添加以下代码 -

<!DOCTYPE html>
<html>
   <head>
      <script type = "text/javascript" src = "https://d3js.org/d3.v4.min.js"></script>
   </head>

   <body>
      <p></p>
      <div></div>
      <script>
         d3.select("p")
         .datum(50)
         .text(function(d) { 
            return "Used existing paragraph element and the data " + d + " is assigned."; 
         });
         
         d3.select("div")
         .datum(100)
         .append("p")
         .text(function(d) { 
            return "Created new paragraph element and the data " + d + " is assigned."; 
         });
      </script>
   </body>
</html>

上述代码的输出如下。

Used existing paragraph element and the data 50 is assigned.

Created new paragraph element and the data 100 is assigned.

data() 方法

data() 方法用于将数据集分配给 HTML 文档中的元素集合。使用选择器选择 HTML 元素后使用它。在我们的列表示例中,我们使用它来设置 li 选择器的数据集。

d3.select("#list").selectAll("li")
   .data([10, 20, 30, 25, 15]);

enter() 方法

enter() 方法输出之前没有图形元素的数据项集。在我们的列表示例中,我们使用它来创建新的 li 元素。

d3.select("#list").selectAll("li")
   .data([10, 20, 30, 25, 15])
   .text(function(d) { return "This is pre-existing element and the value is " + d; })
   .enter()
   .append("li")
   .text(function(d) { return "This is dynamically created element and the value is " + d; });

exit() 方法

exit() 方法输出不再存在数据的图形元素集。在我们的列表示例中,我们使用它通过删除数据集中的数据项来动态删除其中一个 li 元素。

function remove() {
   d3.selectAll("li")
      .data([10, 20, 30, 15])
      .exit()
      .remove()
}

数据功能

在 DOM 操作章节中,我们了解了 D3.js 中的不同 DOM 操作方法,例如 **style()text() 等。这些函数中的每一个通常都以常量值作为参数。然而,在 Data join 的上下文中,它将匿名函数作为参数。此匿名函数获取相应的数据和使用 data() 方法分配的数据集的索引。因此,将为绑定到 DOM 的每个数据值调用此匿名函数。考虑以下 text() 函数。

.text(function(d, i) {
   return d;
});

在此函数中,我们可以应用任何逻辑来操作数据。这些是匿名函数,意味着没有与函数关联的名称。除了 data(d)index(i) 参数之外,我们可以使用 关键字访问当前对象,如下所示 -

.text(function (d, i) {
   console.log(d); // the data element
   console.log(i); // the index element
   console.log(this); // the current DOM object
   return d;
});

请考虑以下示例。

<!DOCTYPE html>
<html>
   <head>
      <script type = "text/javascript" src = "https://d3js.org/d3.v4.min.js"></script>
      <style>
         body { font-family: Arial; }
      </style>
   </head>

   <body>
      <p></p>
      <p></p>
      <p></p>
      <script>
         var data = [1, 2, 3];
         var paragraph = d3.select("body")
         .selectAll("p")
         .data(data)
         .text(function (d, i) {
            console.log("d: " + d);
            console.log("i: " + i);
            console.log("this: " + this);
            return "The index is " + i + " and the data is " + d;
         });
      </script>
   </body>
</html>

上面的脚本将生成以下结果 -

The index is 0 and the data is 1

The index is 1 and the data is 2

The index is 2 and the data is 3

在上面的例子中,参数 d 为你提供数据元素, i 为你提供数组中的数据索引, this 是当前 DOM 元素的引用。在这种情况下,它是段落元素。请注意,我们在上面调用了 .data(data) 函数。 data() 函数为所选元素提供数据,在我们的例子中它是数据数组。