简体   繁体   English

了解D3具有嵌套内容的输入,更新和退出选择

[英]Understanding D3 enter, update and exit selections with nested content

When using D3.js, I find I often want to create elements bound to my data but within divs and wrappers for style/layout reasons. 使用D3.js时,出于样式/布局的原因,我经常发现我想创建绑定到数据但在div和包装器内的元素。 What I'm struggling to understand is how nested content and wrappers works with D3's enter, update, and exit pattern. 我努力了解的是嵌套内容和包装如何与D3的输入,更新和退出模式一起使用。

Basically, I typically use arrays of JSON with a few attributes for each object I want to display. 基本上,我通常对要显示的每个对象使用带有一些属性的JSON数组。

Say I have an array of people, with each person's name and age, that looks something like this: 假设我有一群人,每个人的名字和年龄都看起来像这样:

var array = [
    {
        name: "John",
        age: 31
    },
    {
        name: "Mark",
        age: 29
    },
]

I want to list out this data with D3 in a wrapper div: 我想用包装器div中的D3列出此数据:

<div id="wrapper"></div>

and I would like to create a for each person, and then append a paragraph containing that person's name and a paragraph containing that person's age within that containing . 并且我想为每个人创建一个,然后在包含的内容后追加一个包含该人的姓名的段落和一个包含该人的年龄的段落。 So, the desired end result would be: 因此,所需的最终结果将是:

<div id="wrapper">
    <div class="person">
        <p>John</p>
        <p>31</p>
    </div>
    <div class="person">
        <p>Mark</p>
        <p>29</p>
    </div>
</div>

What I've been trying to do is select all divs within the wrapper div, enter/update based on the data array, and then append the paragraphs with the desired info: 我一直想做的是选择包装div中的所有div,根据数据数组输入/更新,然后在段落中附加所需的信息:

var selection = d3.select("#wrapper")
    .selectAll("div")
    .data(array);

selection.enter()
    .append("div")
    .classed("person", true)
    .merge(selection);

selection.append("p")
    .html(d.name);

selection.append("p")
    .html(d.age);

selection.exit().remove();

I run into a few issues with this: 我遇到了一些问题:

  1. No paragraphs are appended the first time this runs 首次运行时未附加任何段落

  2. If I have an event that adds another person to the array and then runs this again, I see only the two people that were in the original array - John and Mark. 如果我有一个事件将另一个人添加到阵列中,然后再次运行该事件,那么我只会看到原始阵列中的两个人-John和Mark。

  3. If I add another person to the array, not only does issue #2 persist, now it appends paragraphs to those person's wrapper divs so there is duplicate content within the divs for each person. 如果我将另一个人添加到数组中,不仅问题#2仍然存在,现在它还会将段落添加到该人的包装div中,因此每个人的div中都有重复的内容。

Frankly, I'm a little unsure of how the enter/update/exit selection is supposed to work when I want to place D3 elements within wrappers and nest content and add multiple children at the same level and then update the whole group when the array updates. 坦白说,当我想将D3元素放置在包装器中并嵌套内容,并在同一级别添加多个子项,然后在数组中更新整个组时,我不太确定输入/更新/退出选择应该如何工作更新。 Any clarity on this would be greatly appreciated - thank you. 对此的任何澄清将不胜感激-谢谢。

You have to reassign the selection: 您必须重新分配选择:

selection = selection.enter()
  .append("div")
  .classed("person", true)
  .merge(selection);

Also, you cannot use the first argument like that, you have to pass an function to the methods: 同样,您不能使用这样的第一个参数,必须将一个函数传递给方法:

selection.append("p")
  .html(d=>d.name);

selection.append("p")
  .html(d=>d.age);

Here is your code with those changes: 这是您所做的更改的代码:

 var array = [{ name: "John", age: 31 }, { name: "Mark", age: 29 }, ] var selection = d3.select("#wrapper") .selectAll("div") .data(array); selection = selection.enter() .append("div") .classed("person", true) .merge(selection); selection.append("p") .html(d=>d.name); selection.append("p") .html(d=>d.age); selection.exit().remove(); 
 <script src="https://d3js.org/d3.v5.min.js"></script> <div id="wrapper"></div> 

Finally, given the information you shared in your comment , the first choice to fix your code seems to be creating a nested enter/update/exit selection. 最后,鉴于您在注释中共享的信息,解决代码的第一选择似乎是创建嵌套的enter / update / exit选择。

However, on a second thought, because your objects don't have nested arrays, a way simpler solution is appending the <p> elements in the enter selection and updating them in the update selection. 但是,再考虑一下,因为您的对象没有嵌套数组,所以一种更简单的解决方案是将<p>元素添加到enter选择中,然后在update选择中对其进行更新。

Here is a snippet demonstrating this, with 3 different data arrays: 这是一个演示此内容的代码段,其中包含3个不同的数据数组:

 var array = [{ name: "John", age: 31 }, { name: "Mark", age: 29 }, ] function display() { var selection = d3.select("#wrapper") .selectAll("div") .data(array); var selectionEnter = selection.enter() .append("div") .classed("person", true); selectionEnter.append("p") .classed("name", true) .html(d => d.name); selectionEnter.append("p") .classed("age", true) .html(d => d.age); selection.exit().remove(); selection.select(".name") .html(d => d.name); selection.select(".age") .html(d => d.age); } display(); setTimeout(function() { array = [{ name: "John", age: 31 }, { name: "Mark", age: 29 }, { name: "Andrew", age: 24, } ]; display(); }, 2000) setTimeout(function() { array = [{ name: "John", age: 21 }, { name: "Bob", age: 49 } ]; display(); }, 4000) 
 <script src="https://d3js.org/d3.v5.min.js"></script> <div id="wrapper"></div> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM