如果在 D3.js 中的第二个参数指定数据方法,则现有元素未定义

If you specify a data method the second argument in D3.js, existing elements are undefined

如果在 D3.js 中的第二个参数指定数据方法,则现有元素未定义

<div id="hoge">
  <p>C</p>
  <p>A</p>
  <p>Z</p>  
  <p>X</p>
</div>

<script>
dataset = [4,9];
d3.select("#hoge").selectAll("p").data(dataset, function(d,i){
      console.log(d);
      return d;
   })
  .enter()
  .append("p")
  .text(function(d){
   return d;
  })


console.log

UNDEFINED
UNDEFINED
UNDEFINED
UNDEFINED
4
9


为什么未定义?
为什么不关注?

C
A
Z
X
4
9

因为如果您为 .data 函数指定第二个参数,则该参数的结果将是绑定到元素的值。没有第二个参数,数据由索引绑定。因为您要返回 d,所以它将受值约束。由于没有数据绑定到现有的 p 元素,它们的数据是 undefined,与您的 dataset 数组中的任何值都不匹配,因此它会处理您的所有数据dataset 数组作为新值,为它们创建新的占位符 p 元素。未定义的将在 exit 选项中。

有关详细说明,请参阅 https://github.com/mbostock/d3/wiki/Selections#data

当我阅读@Ben Lyall 的回答时我仍然不清楚(抱歉 Ben,不是你,是我......)并且我通过阅读 wiki 肯定不清楚(我认为他们弄反了),所以我通过代码进行追踪以查看发生了什么...

首先,值得记住的是 selectiongroups 的数组,groupnodes 的数组.如果有多个group,相同的数据绑定到所有groups.

将数据绑定到每个 group 时,d3 在两个单独的阶段调用 key 函数,首先构建一个组中所有节点的关联数组,使用 key 函数中的 returned 值作为键,第二个导出每个数据数组元素的键。然后用于查找节点数组,如果查找成功,则将数据元素绑定到其匹配节点,并将该节点添加到更新选择中。

在第一阶段,keynode 中的每个 node 调用一次=15=],this 上下文设置为 节点 d参数是节点上的绑定数据i参数是[=77]节点的索引=]节点在group。每个 node 的值 returned 和 node 本身被添加到 collection以节点为值的键值对和 key 函数的 return 值作为键。

在第二个阶段中,key 在第一个阶段中对每个 element 调用一次数据数组的维度,this 上下文设置为数据数组。 d 参数是数据数组的 元素 i 参数是数据数组中元素的索引。值 returned 用于查找集合,如果找到匹配项,则将该节点放入更新选择中。

因此,正如@Ben Lyall所说,key函数中的d,就[=63而言=]defining keys,是之前绑定在节点上的数据。 (d3 在节点上添加了一个 __data__ 成员)。当key用于限定数据元素时,d为数据数组元素。这就是为什么您会看到四个节点的 undefined 和两个数据元素的预期值。

也就是说,没有理由认为密钥必须是 d 的函数。

试试这个...

<div id="hoge">
    <p>C</p>
    <p>A</p>
    <p>Z</p>
    <p>X</p>
</div>

<script>
    dataset = [4, 9];
    d3.select("#hoge").selectAll("p").data(dataset, function (d, i) {
        d = d || this.textContent;
        console.log(d);
        return d;
    })
      .enter()
      .append("p")
      .text(function (d) {
          return d;
      })
</script>

在phase1中,d是绑定数据,没有定义,所以关键函数returns是节点的文本。在 phase2 中,d 是数据值,因此它们是 returned。因为phase2中的none个key与phase1中的key匹配,所以没有更新组。

矫枉过正,你也可以把它和这个混为一谈...

        d = d || dataset[i];

因为 key 函数 return 在两个阶段中 i = 0 和 1 的值相同,第一个2个节点被认为是更新节点,因为所有的数据都是根据key函数绑定的,所以enter组是空的。

但最有用的情况是如果您有这样的数据...

dataset = [4, 9, 'X', 'C'];
d3.select("#hoge").selectAll("p").data(dataset, function (d, i) {
    d = d || this.textContent;
    console.log(d);
    return d;
}).attr('class', 'update')
  .enter()
  .append("p").attr('class', 'enter')
  .text(function (d) {
      return d;
  })

这是结果...

<div id="hoge"> <p class="update">C</p> <p>A</p> <p>Z</p> <p class="update">X</p> <p class="enter">4</p> <p class="enter">9</p> </div>

在阶段 1 中,所有节点都包含在以文本内容为键的集合中。在阶段2中,数据值用于查找集合,并且在C和X上有匹配,因此它们在更新集合中,没有匹配的数字在输入选择中。

您可能应该只使用 .data(dataset),然后这会将数据绑定到前两段。剩余的两个元素应该在 .exit() 中可用,因此您可以 .remove() 它们或其他东西(也许向它们添加一些其他数据,如默认数据)。