正确使用 D3 “data” 方法,并传递函数和数据集

Properly using D3 `data` method with a function and data set passed

本文关键字:方法 传递函数 数据集 data D3      更新时间:2023-09-26

D3教程(部分)损坏

在我看来,D3 中似乎发生了一些变化。

这样说是因为,当我试图更好地理解数据绑定方法时,我查看了 Mike Bostock 的选择教程页面。 这个页面本身不能完全工作(不再),我敢肯定,当他制作它时它工作得很好!!

例:

大约 3/4 的页面,给出了以下代码示例:

var letters = [
  {name: "A", frequency: .08167},
  {name: "B", frequency: .01492},
  {name: "C", frequency: .02780},
  {name: "D", frequency: .04253},
  {name: "E", frequency: .12702}
];
function name(d) {
  return d.name;
}
d3.selectAll("div").data(letters, name);

如果我完全在页面本身的控制台中运行它,我会在控制台中得到以下内容:

Hooray, you opened the JavaScript console. Have fun!
var div = d3.selectAll("div").data(vowels, name);
(index):165 Uncaught TypeError: Cannot read property 'name' of undefined
    at HTMLDivElement.name (https://bost.ocks.org/mike/selection/:165:11)
    at e (https://d3js.org/d3.v3.min.js:3:13654)
    at Array.Co.data (https://d3js.org/d3.v3.min.js:3:14326)
    at <anonymous>:1:31name @ (index):165e @ d3.v3.min.js:3Co.data @ d3.v3.min.js:3(anonymous function) @ VM3012:1

(这是在Max OS X 10.11和Google Chrome上,但这应该是无关紧要的,因为问题不依赖于机器,如下所述。


Data(绑定)方法的误解

但这是真正让我感到困惑的事情:

  • 了解,从此页面上的 Mike 自己的文档中,无论如何,这应该不起作用。 data 方法基本上创建 2 个层次结构级别:单个"组",然后是组下的一堆元素。
    • 因此,该方法创建 1 组和 5 个元素,元素包含数据。 然后,我们尝试在数据中查找属性,但在组级别,而不是元素级别。 我理解对吗?
  • 但是,如果我们只是传递
  • 数据,或者只是传递一个解释数据的函数,例如 d3.selectAll("div").data(d3.entries(letters)) ,那么事情又可以正常工作。

所以似乎显然有两件事正在发生:

  • 我自己对data方法如何工作或应该如何工作的这种微妙之处的误解。
  • D3 本身中的一些更改,使得本教程无法再像以前那样运行。

这仍然全部使用 D3 的 V3,正如该页面本身的源代码所说:

<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script></script>

我打算将其作为这个问题的副本关闭,但我认为它需要更多的解释。

简而言之,这是预期的行为。 您基本上具有现有的div,没有绑定任何数据。 键函数尝试在它们上执行,d未定义,你会得到你的uncaught typerror。 以下是简化版本:

var letters = [
  {name: "A", frequency: .08167},
  {name: "B", frequency: .01492},
  {name: "C", frequency: .02780},
  {name: "D", frequency: .04253},
  {name: "E", frequency: .12702}
];
function name(d) {
  return d.name;
}
d3.selectAll("div").data(letters, name);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>

但是等等,这意味着教程被严重破坏了,对吧?

不,您看到这一点是因为您无地执行它。 上一步让您在没有key函数的情况下将数据绑定到这些div。 然后,当您与 key 函数重新绑定时,这些div 上有现有数据(d已定义),您的问题就消失了:

var numbers = [4, 5, 18, 23, 42];
      
var letters = [
  {name: "A", frequency: .08167},
  {name: "B", frequency: .01492},
  {name: "C", frequency: .02780},
  {name: "D", frequency: .04253},
  {name: "E", frequency: .12702}
];
function name(d) {
  return d.name;
}
d3.selectAll("div").data(numbers);
d3.selectAll("div").data(letters, name);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>

但是等等,这很糟糕,d3不应该为我处理这个问题吗?

不,因为在现实生活中d3使用中,你永远不会真正看到这一点。您不会有预先存在的<div>来绑定数据。 相反,您将使用 d3 来管理这些<div>的整个生命周期(完整的进入、退出、更新周期)。 简而言之,这些div 将始终是数据绑定的,否则您将永远不需要它们。

      var letters = [
        {name: "A", frequency: .08167},
        {name: "B", frequency: .01492},
        {name: "C", frequency: .02780},
        {name: "D", frequency: .04253},
        {name: "E", frequency: .12702}
      ];
      
      function name(d) {
        return d.name;
      }
      d3.select("body")
        .selectAll("div")
        .data(letters, name)
        .enter()
        .append("div")
        .html(function(d){ return d.name });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>