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)
如果我想象背后的逻辑,那就是:
步 # | 行动 | 输入选择 | 更新选择 | 退出选择 | 加入返回选择(回车+更新) |
---|---|---|---|---|---|
0 | / | 【黑、白、棕】 | [] | [] | 黑色、白色、棕色 |
1 | 添加“黄色”颜色 | [黄色] | 黑、白、棕] | [] | 黑色、白色、棕色、黄色] ? |
2 | 删除索引为 0 的元素 | [] | [黄色] | [黑色的] | [黄色] |
但是好像不对啊,我哪里错了?
我知道这样做data(myData, (d, i) => i)
是一样的data(myData)
,这意味着 D3 通过索引匹配数据/DOM 节点。那么为什么如果我查看__data__
绑定元素的属性,它们有__data__ = black/white/brown
而没有__data__ = 0/1/2
呢?
我很困惑,我没有找到任何可以帮助我的东西..
我阅读了 D3 文档以及这个问题。
如果我尝试删除索引为 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 函数应返回一个字符串)。这样,无论索引如何,相同的数据都会与相同的节点配对。因此,从数组中拼接数据将删除它所绑定的相应节点。
我创建了两种可视化键函数的尝试,一种是依赖于数据的键,一种是基于索引的键。如果仔细观察,当索引键用于匹配数据和元素时,绑定到元素的数据会发生变化:键独立于数据。
这些块有点粗糙,我可能会调整并合并到这里的答案正文中。