简体   繁体   中英

How to update multiple elements for each data

There are a lot of articles about how to update dom for each data. But what if I want to update multiple dom nodes for each data.

For example, for each data, generate div and span according to the number in the data.

[
  [1, 2],
  [2, 1],
]

I want to yield like

<div class="data">
  <div>1</div><span>2</span><span>2</span>
</div>
<div class="data">
  <div>2</div><div>2</div><span>1</span>
</div>

Should start with

const selection = d3.selectAll("div.data");
selection.exit().remove();
selection.enter().append("div").attr("class", "data").merge(selection); 
// Then I don't know how to add multiple dom according to the data value.

First of all, your question's title (" how to update multiple elements for each data" ) suggests that you want to update those elements, when in fact, by reading your actual question, you just want to create them ( enter , in D3 lingo). Also, pay attention to the fact that you cannot use data() following d3.selectAll() .

That being said, there are several solutions. One of them is using each() for your outer divs, and then appending how many elements you want (divs or spans) by using d3.range() , which will create an array whose length is based on the number you pass to it. The actual content of that array is irrelevant, since the text in both divs and spans correspond to the numbers in the inner arrays.

Here is a simple example, pay attention to the colors for each element (outer divs, inner divs and spans):

 const body = d3.select("body"); const data = [ [1, 2], [2, 1], ]; const dataDiv = body.selectAll(null).data(data).enter().append("div").attr("class", "data").each(function(d) { d3.select(this).selectAll(null).data(d3.range(d[0])).enter().append("div").attr("class", "innerDiv").html(d[0]); d3.select(this).selectAll(null).data(d3.range(d[1])).enter().append("span").html(d[1]); })
 .data { margin: 10px; padding: 2px; display: inline-block; background-color: wheat; }.innerDiv { margin: 10px; padding: 2px; display: inline-block; background-color: yellowgreen; } span { margin: 10px; padding: 2px; background-color: salmon; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

And here is the generated HTML:

<div class="data">
  <div class="innerDiv">1</div><span>2</span><span>2</span>
</div>
<div class="data">
  <div class="innerDiv">2</div>
  <div class="innerDiv">2</div><span>1</span>
</div> 

d3 supports nesting of selections . The critical bit here is after binding data to the parent elements, call parent.selectAll() and then bind the data using a function instead of a variable, eg, parent.selectAll(child).data(d => d) . d3 will automatically bind the nested arrays to each child element. This is described in the link , which defines a table with nested tr and td elements:

The data operator defines the array of data for each group. Here the data function is invoked once per row and successively passed each parent datum. Since the parent data is an array of arrays, the data function simply returns the inner array for each row of cells.

 let data = [ [1, 2], [2, 1], ] let selection = d3.selectAll('div.data').data(data).enter().append("div").attr("class", "data") selection.selectAll('span').data(function(d) { return d; }).enter().append('span').text(d => d)
 .data span { background: white; border: 1px solid black; padding: 5px; margin: 5px; }.data { background: yellow; margin: 10px; border: 1px solid red; padding: 8px; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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