d3.join() 输入调用而不是更新?
d3.join() enter called instead of update?
我试图让 D3 每秒更新一次 SVG 元素,方法是将一个新的 Date
对象传递给 data()
函数,然后是 join()
来更新页面。我实际上是在 Observable notebook using Promises.tick()
but I think the code below (full source) 中执行此操作是相关位的粗略近似值。我的最终目标是制作一个时钟,但此处的代码是我遇到的问题的最小演示。
此代码的问题在于 join()
中的 update
函数从未被调用——每次循环时,都会调用 enter
函数,这会导致每次都会添加新的 text
。因为我有一个数据和一个元素,所以我希望 join
的 update
被调用,而不是 enter
。我知道 data()
(example) 上的键函数对于确定发生了什么变化很重要,但我的理解是默认键是数组索引,在这里它应该始终为 0。无论如何,指定不同的关键函数(从标识函数到返回常量)都没有帮助。
while (true) {
var data = [new Date()];
svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.selectAll("text")
.data(data)
.join(
enter => enter.append("text").text(d => "enter: " + d),
update => update.text(d => "update: " + d)
);
await new Promise(resolve => setTimeout(resolve, 1000));
}
看看这个:
svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
//etc...
每次 运行 循环以及之后的所有选定元素(文本或其他)时,您都在 附加 一个新的 <g>
元素将引用新添加的组,即 selectAll
选择所有文本 inside 新添加的组。显然,有none。这就是为什么你总是只有一个输入选择,而没有更新选择。
看看这个演示,重现您的问题:
var body = d3.select("body");
function foo(data) {
var div = body.append("div")
.selectAll("p")
.data(data)
.join(
enter => enter.append("p").html(d => "enter: " + d),
update => update.html(d => "update: " + d)
);
};
foo([Math.random()]);
d3.interval(function() {
foo([Math.random()])
}, 1000)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.8.0/d3.min.js"></script>
假设您只想附加组一次,您可以这样做:
var g = svg.selectAll(".myGroup")
.data([true]);
g = g.enter()
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.merge(g);
g.selectAll("text")
.data(data)
.join(
enter => enter.append("text").text(d => "enter: " + d),
update => update.text(d => "update: " + d)
);
这里,data([true])
表示数据本身无关紧要,它只是一个真值,用于附加单个组元素。
这是一个展示其工作原理的演示:
var body = d3.select("body");
function foo(data) {
var div = body.selectAll(".myDiv")
.data([true]);
div = div.enter()
.append("div")
.attr("class", "myDiv")
.merge(div);
div.selectAll("p")
.data(data)
.join(
enter => enter.append("p").html(d => "enter: " + d),
update => update.html(d => "update: " + d)
);
};
foo([Math.random()])
d3.interval(function() {
foo([Math.random()])
}, 1000)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.8.0/d3.min.js"></script>
我试图让 D3 每秒更新一次 SVG 元素,方法是将一个新的 Date
对象传递给 data()
函数,然后是 join()
来更新页面。我实际上是在 Observable notebook using Promises.tick()
but I think the code below (full source) 中执行此操作是相关位的粗略近似值。我的最终目标是制作一个时钟,但此处的代码是我遇到的问题的最小演示。
此代码的问题在于 join()
中的 update
函数从未被调用——每次循环时,都会调用 enter
函数,这会导致每次都会添加新的 text
。因为我有一个数据和一个元素,所以我希望 join
的 update
被调用,而不是 enter
。我知道 data()
(example) 上的键函数对于确定发生了什么变化很重要,但我的理解是默认键是数组索引,在这里它应该始终为 0。无论如何,指定不同的关键函数(从标识函数到返回常量)都没有帮助。
while (true) {
var data = [new Date()];
svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.selectAll("text")
.data(data)
.join(
enter => enter.append("text").text(d => "enter: " + d),
update => update.text(d => "update: " + d)
);
await new Promise(resolve => setTimeout(resolve, 1000));
}
看看这个:
svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
//etc...
每次 运行 循环以及之后的所有选定元素(文本或其他)时,您都在 附加 一个新的 <g>
元素将引用新添加的组,即 selectAll
选择所有文本 inside 新添加的组。显然,有none。这就是为什么你总是只有一个输入选择,而没有更新选择。
看看这个演示,重现您的问题:
var body = d3.select("body");
function foo(data) {
var div = body.append("div")
.selectAll("p")
.data(data)
.join(
enter => enter.append("p").html(d => "enter: " + d),
update => update.html(d => "update: " + d)
);
};
foo([Math.random()]);
d3.interval(function() {
foo([Math.random()])
}, 1000)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.8.0/d3.min.js"></script>
假设您只想附加组一次,您可以这样做:
var g = svg.selectAll(".myGroup")
.data([true]);
g = g.enter()
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.merge(g);
g.selectAll("text")
.data(data)
.join(
enter => enter.append("text").text(d => "enter: " + d),
update => update.text(d => "update: " + d)
);
这里,data([true])
表示数据本身无关紧要,它只是一个真值,用于附加单个组元素。
这是一个展示其工作原理的演示:
var body = d3.select("body");
function foo(data) {
var div = body.selectAll(".myDiv")
.data([true]);
div = div.enter()
.append("div")
.attr("class", "myDiv")
.merge(div);
div.selectAll("p")
.data(data)
.join(
enter => enter.append("p").html(d => "enter: " + d),
update => update.html(d => "update: " + d)
);
};
foo([Math.random()])
d3.interval(function() {
foo([Math.random()])
}, 1000)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.8.0/d3.min.js"></script>