了解 D3 数据绑定键访问器

whi*_*cle 5 html javascript d3.js

我是 D3 的新手,我正在做一个简单的例子,试图了解数据绑定的工作原理。

基本上我有一个颜色数组,一个用于添加颜色的函数和一个从索引中删除颜色的函数。不起作用的是删除操作。如果我将 0 设置为要删除的索引,我会看到 D3 将最后一个元素设置为要删除的元素。如果我使用 key accessor d => d,它就可以工作。我有很多问题。

这是我的代码:

const data = {
  colors: ["Black", "White", "Brown"],
  addColor(color) {
    this.colors.push(color);
  },
  removeColorByIndex(index) {
    this.colors.splice(index, 1);
  }
};

const root = d3.select("#root");
const barsContainer = d3.select("#bars-container");
const addButton = d3.select("#add-button");
const removeButton = d3.select("#remove-button");

addButton.on("click", () => {
  const newColor = d3.select("#color-input").node().value;
  data.addColor(newColor);
  update();
});
removeButton.on("click", () => {
  const index = d3.select("#index-input").node().value;
  data.removeColorByIndex(index);
  update();
});

function update() {
  barsContainer
    .selectAll("div")
    .data(data.countries, (d, i) => {
      console.log({ i, d });
      return i;
    })
    .join(
      (enter) => {
        console.log("enter:", enter);
        return enter
          .append("div")
          .text((d) => d)
          .classed("bar", true)
          .classed("added", true);
      },

      (update) => {
        console.log("update:", update);
        return update.classed("update", true);
      },

      (exit) => {
        console.log("exit:", exit);
        return exit.classed("remove", true);
      }
    );

  console.log("divs", barsContainer.selectAll("div")["_groups"][0]);
}

update();
Run Code Online (Sandbox Code Playgroud)
.bar {
  margin: 5px 0px 5px 0px;
  max-width: 200px;
  padding: 10px;
}
.added { background-color: lightgreen; }
.update { background-color: cornflowerblue; }
.remove { background-color: tomato; }
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="root">
  <div>
    <div>
      <input type="text" id="color-input" />
      <button id="add-button">Add color</button>
    </div>
    <div>
      <input type="text" id="index-input" />
      <button id="remove-button">Remove color by index</button>
    </div>
  </div>
  <div id="bars-container"></div>
</div>
Run Code Online (Sandbox Code Playgroud)

  1. 最初所有的条都是绿色的。那是因为它们都在输入选择中。
  2. 如果我使用 UI 按钮添加颜色,则会出现新的条形,它是绿色的,旧的变成蓝色。
  3. 然后,如果我尝试删除索引为 0 的条形,最后一个条形变为红色,而不是第一个,为什么?

如果我想象背后的逻辑,那就是:

步 # 行动 输入选择 更新选择 退出选择 加入返回选择(回车+更新)
0 / 【黑、白、棕】 [] [] 黑色、白色、棕色
1 添加“黄色”颜色 [黄色] 黑、白、棕] [] 黑色、白色、棕色、黄色] ?
2 删除索引为 0 的元素 [] [黄色] [黑色的] [黄色]

但是好像不对啊,我哪里错了?

我知道这样做data(myData, (d, i) => i)是一样的data(myData),这意味着 D3 通过索引匹配数据/DOM 节点。那么为什么如果我查看__data__绑定元素的属性,它们有__data__ = black/white/brown而没有__data__ = 0/1/2呢?

我很困惑,我没有找到任何可以帮助我的东西..

我阅读了 D3 文档以及这个问题

And*_*eid 2

如果我尝试删除索引为 0 的条形图,最后一个条形图会变成红色 [退出],而不是第一个,为什么?

key 函数返回的标识符既不存储在 DOM 节点上,也不存储在数据上。每次使用时都会对其进行评估.data()。每次使用.data()key 函数(如果提供)时,都会对选择中的每个节点进行评估(使用绑定 datum .__data__,它表示数据数组中的一项),然后 D3 迭代数据数组以查找与节点。如果未找到匹配节点,则会将节点添加到具有该数据的输入选择中。如果匹配完所有数据后,还有多余的节点,则退出。

您的关键功能是(d,i)=>i- 通过删除数据数组中的第一项,数据数组中仍然存在索引为 0 的项目。索引为 0 的数据数组项与选择中的第 0 个节点相匹配。因此第一个节点无法退出:它在数据数组中仍然有相应的项。

给定您的关键函数:这意味着最初位于索引 1 处并与索引 1 处的节点配对的数据现在是索引 0 处的数据(拼接后)并将与索引 0 处的节点配对

由于您的数据数组比原来短了一项,因此 DOM 中存在一个多余的节点。最后一个节点的索引最高,没有匹配的数据数组项,因此退出最后一个节点。除最后一个节点外的所有节点都在更新选择中。

通常,您会使用关键函数来引用数据本身,而不是其位置/索引(因为这可能会由于排序或其他因素而改变,从而改变哪个数据绑定到哪个节点)。因此,如果您的数据由唯一的颜色名称组成,您需要使用:(.data(data,d=>d)注意:如果 d 是一个对象,则 key 函数应返回一个字符串)。这样,无论索引如何,相同的数据都会与相同的节点配对。因此,从数组中拼接数据将删除它所绑定的相应节点。

我创建了两种可视化键函数的尝试,一种是依赖于数据的键,一种是基于索引的键。如果仔细观察,当索引键用于匹配数据和元素时,绑定到元素的数据会发生变化:键独立于数据。

基于数据的密钥

基于索引的键

这些块有点粗糙,我可能会调整并合并到这里的答案正文中