javascript - 使用 DOM 子节点和数据传播进行更新
问题描述
我想通过数据传播更新包含子节点的 DOM。
第一次填充 DOM 时,数据会正确传播给它们的子级。但是,当我用新数据更新父 DOM 时,子 DOM 不会随之更新。
同样令人惊讶的是,旧数据可以从子元素中检索,我强烈反对,因为数据太大而且我不希望保留旧/额外数据,它保存引用并保留旧数据。(在我的意义上内存泄漏)
const DATA = [{
name: "data1",
left: 70,
top: 70,
radius: 20
}, {
name: "data2",
left: 200,
top: 100,
radius: 50
}];
const svg = d3.select("#svg");
// Not working as expected
function loadData(data) {
console.log(data);
const container = svg.selectAll(".container")
.data(data);
container.exit().remove();
const enter = container.enter().append("g")
.attr("class", "container");
enter.append("circle");
enter.append("text");
container.merge(enter)
.attr("transform", entry => `translate(0,${entry.top})`)
svg.selectAll(".container circle")
.attr("r", entry => entry.radius)
.attr("cx", entry => entry.left);
svg.selectAll(".container text")
.text(entry => entry.name);
}
// Working, but not desired
function expected(data) {
console.log(data);
const container = svg.selectAll(".container")
.data(data);
container.exit().remove();
const enter = container.enter().append("g")
.attr("class", "container");
enter.append("circle");
enter.append("text");
container.merge(enter)
.attr("transform", entry => `translate(0,${entry.top})`)
svg.selectAll(".container circle")
.data(data)
.attr("r", entry => entry.radius)
.attr("cx", entry => entry.left);
svg.selectAll(".container text")
.data(data)
.text(entry => entry.name);
}
let i = 0;
loadData(DATA);
function updateData() {
i = (i+1) % 2;
loadData(DATA.slice(i,i+1));
}
function expectedUpdate() {
i = (i+1) % 2;
expected(DATA.slice(i,i+1));
}
button {
display: inline-block;
}
svg {
width: 300px;
height: 300px;
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<button onclick="updateData();">Update</button>
<button onclick="expectedUpdate();">Expected</button>
<svg id="svg"></svg>
这只是演示,我有一个父<g>
元素,还有 2个子元素<circle>
&<text>
附加到它上面。
第一次渲染时,我使用该loadData()
方法填充 DOM,相应的子 DOM(圆圈和文本)能够检索插入到其父 DOM (g) 中的数据,这显示了沿 DOM 树的正确数据传播.
但是,当我多次按下“更新”按钮时,只有父 DOM 正在更新,子 DOM 能够访问不存在的数据(旧数据)。
“预期”按钮显示了预期的行为,但我可以看到数据被多次绑定,作为一名 javascript 工程师,我非常害怕这种数据绑定。
解决方案
您的非工作功能 ( ) 中的主要问题loadData
是您正在使用selectAll
. 与, 不同select
,selectAll
不传播数据。
看一下我总结的这张表,总结了它们之间的区别:
方法 | 选择() | 全选() |
---|---|---|
选择 | 选择与选择器字符串匹配的第一个元素 | 选择与选择器字符串匹配的所有元素 |
分组 | 不影响分组 | 影响分组 |
数据传播 | 传播数据 | 不传播数据 |
如您所见,您的主要信息是:
select
:传播数据。selectAll
:不传播数据。
鉴于您的片段每组只有一个selectAll
孩子(圆圈和文本),最简单的解决方案就是更改为select
. 但是,如果每个父母有多个孩子,最好的(也是惯用的)解决方案是创建一个嵌套的进入-更新-退出选择,这有点费力。
除此之外,您还缺少关键功能:
const container = svg.selectAll(".container")
.data(data, function(d) {
return d.name;
});
这是您进行这些更改的代码:
const DATA = [{
name: "data1",
left: 70,
top: 70,
radius: 20
}, {
name: "data2",
left: 200,
top: 100,
radius: 50
}];
const svg = d3.select("#svg");
// Not working as expected
function loadData(data) {
console.log(data);
const container = svg.selectAll(".container")
.data(data, function(d) {
return d.name;
});
container.exit().remove();
const enter = container.enter().append("g")
.attr("class", "container");
enter.append("circle");
enter.append("text");
container.merge(enter)
.attr("transform", entry => `translate(0,${entry.top})`)
svg.select(".container circle")
.attr("r", entry => entry.radius)
.attr("cx", entry => entry.left);
svg.select(".container text")
.text(entry => entry.name);
}
// Working, but not desired
function expected(data) {
console.log(data);
const container = svg.selectAll(".container")
.data(data);
container.exit().remove();
const enter = container.enter().append("g")
.attr("class", "container");
enter.append("circle");
enter.append("text");
container.merge(enter)
.attr("transform", entry => `translate(0,${entry.top})`)
svg.selectAll(".container circle")
.data(data)
.attr("r", entry => entry.radius)
.attr("cx", entry => entry.left);
svg.selectAll(".container text")
.data(data)
.text(entry => entry.name);
}
let i = 0;
loadData(DATA);
function updateData() {
i = (i + 1) % 2;
loadData(DATA.slice(i, i + 1));
}
function expectedUpdate() {
i = (i + 1) % 2;
expected(DATA.slice(i, i + 1));
}
button {
display: inline-block;
}
svg {
width: 300px;
height: 300px;
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<button onclick="updateData();">Update</button>
<button onclick="expectedUpdate();">Expected</button>
<svg id="svg"></svg>
推荐阅读
- enterprise-architect - EA在生成XML时找不到指定的路径
- php - QC 中的域计数
- node.js - 角度同时从 formData 和 formGroup 发送数据
- asp.net - 在 ASP.NET MVC 操作中使用数据类传递许多参数
- powerpivot - 等级关系 权力支点
- automated-tests - 如何将空字符串传递给katalon studio中的输入元素
- java - 为 X DevAPI 禁用 MySQL 连接器/J SSL
- regex - 如何仅为特定分支和标签运行 Gitlab CI?
- css - 字体大小尽可能大以适应窗口宽度
- excel - Power Query - 根据单元格中电子邮件的出现创建和填充列 Email1、Email2 ...