在两个级别上输入/更新/退出,无需嵌套

时间:2019-05-15 19:00:30

标签: d3.js

要对相应集合执行输入/更新/退出操作,我们可以编写以下代码,以Bostock的示例为例。

每个数字都出现在p节点中。

function update(data) {
  var text = d3.select('body')
    .selectAll('p')
    .data(data);

  text.enter()
    .append('p')
    .merge(text)
    .text(d => d);

  text.exit().remove();
}
update([1, 2]);
update([3, 4, 5, 6]);
update([7, 8, 9]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

现在假设我们希望每个p节点本身都在div节点内。这应该是一个简单的扩展,因为我们不需要嵌套。但是以下明显的修改是有缺陷的。

function update(data) {
  var text = d3.select('body')
    .selectAll('div')
    .data(data);

  text.enter()
    .append('div')
    .append('p')
    .text(d => d)
    .merge(text);

  text.exit()
    .remove();
}
update([1, 2]);
update([3, 4, 5, 6]);
update([7, 8, 9]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

为什么?

1 个答案:

答案 0 :(得分:1)

由于更新选择是指<div>元素,而不是<p>元素,因此请删除merge()方法。当然,这具有需要重复代码(特别是.text(d => d))的不良影响:

text.enter()
    .append('div')
    .append('p')
    .text(d => d);

text.select('p')
    .text(d => d);

这是您所做的更改的代码:

function update(data) {
  var text = d3.select('body')
    .selectAll('div')
    .data(data);

  text.enter()
    .append('div')
    .append('p')
    .text(d => d);

  text.select('p')
    .text(d => d);

  text.exit()
    .remove();
}
update([1, 2]);
setTimeout(function() {
  update([3, 4, 5, 6])
}, 1000);
setTimeout(function() {
  update([7, 8, 9])
}, 2000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

另一种方法(很尴尬)是创建div的简单选择,并使用html在段落中附加以下值:

text.enter()
    .append('div')
    .merge(text)
    .html(d => '<p>'+ d + '</p>'); 

请注意,您必须像在第一个摘要中一样,在之后 merge之后设置值。

这是运行代码:

function update(data) {
  var text = d3.select('body')
    .selectAll('div')
    .data(data);

  text.enter()
    .append('div')
    .merge(text)
    .html(d => '<p>' + d + '</p>');

  text.exit()
    .remove();
}

update([1, 2]);
setTimeout(function() {
  update([3, 4, 5, 6])
}, 1000);
setTimeout(function() {
  update([7, 8, 9])
}, 2000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

最后,关于您的问题:

  

为什么?

如果您将text移到merge之后,您的第二个摘录种类会起作用:

function update(data) {
  var text = d3.select('body')
    .selectAll('div')
    .data(data);

  text.enter()
    .append('div')
    .append('p')
    .merge(text)
    .text(d => d);

  text.exit()
    .remove();
}
update([1, 2]);
setTimeout(function() {
  update([3, 4, 5, 6])
}, 1000);
setTimeout(function() {
  update([7, 8, 9])
}, 2000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

但是,如您所见,它将删除更新中的内部<p>元素。发生这种情况是因为您的更新选择是对div的选择,因此当您使用text(内部使用textContent)时,实际上是在删除以前附加到这些div上的所有内容。


PS:在两个S.O.中代码段,请确保未选中show console,否则代码会将其选择为<div>(S.O。代码段使用<div>来显示控制台)。