简体   繁体   中英

D3 Nested append with update, selection issues

D3 selections bake my noodle!

I have a series of nested selections that need to be updated based on input fields. I have 3 out of the 4 working. The main issues is with the 3rd selection update innerSArray , the yellow bars are not updating. I think the issues is coming from further up the chain, but not sure. I had to .merge() previous selects.

To update a nested .append() , I've done a new d3.selectAll() , but am not sure this is the correct way of doing this. I've done this for innerM and innerSM .

How can I get the 3rd select updating properly? Should I be breaking the nested appends apart?

 .outer { border: 1px solid black; background-color: grey; width: 100%; height: 100px; position: relative; display: flex; } .inner { display: flex; justify-content: flex-start; border: 1px solid blue; background-color: cyan; height: 50px; position: relative; } .innerM { display: flex; border: 1px solid blue; background-color: magenta; height: 25px; position: relative; } .sr { display: flex; background-color: yellow; height: 12px; } .sm { display: flex; background-color: black; height: 6px; } 
  <!doctype html> <html> <head> <meta charset="UTF-8"> <title>#</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <script src="https://d3js.org/d3.v5.min.js"></script> <script src="https://d3js.org/d3-selection-multi.v1.min.js"></script> </head> <body> <p>Inner: <input id="increment" type="number" value="1" step="1" max="5" /></p> <p>InnerM: <input id="incrementM" type="number" value="1" step="1" max="5" /></p> <p>SR: <input id="incrementSR" type="number" value="1" step="1" max="5" /></p> <p>SM: <input id="incrementSM" type="number" value="1" step="1" max="5" /></p> <div id="anchor"></div> <script> const increment = document.getElementById('increment'); const incrementM = document.getElementById('incrementM'); const incrementSR = document.getElementById('incrementSR'); const incrementSM = document.getElementById('incrementSM'); const anchor = d3.select('#anchor'); const data = [ { "outer": [ { "inner": [ { "r": 40, "m": 10, "s": [] }, { "r": 70, "m": 13, "s": [] }, { "r": 10, "m": 15, "s": [ { "r": 20, "m": 5 } ] }, { "r": 15, "m": 9, "s": [] }, { "r": 52, "m": 20, "s": [] }, { "r": 96, "m": 30, "s": [ { "r": 50, "m": 10 } ] }, { "r": 192, "m": 60, "s": [] }, { "r": 301, "m": 50, "s": [] } ] } ] } ]; increment.addEventListener('change', function() { update(data); }); incrementM.addEventListener('change', function() { update(data); }); incrementSR.addEventListener('change', function() { update(data); }); incrementSM.addEventListener('change', function() { update(data); }); function update(data) { // main data let root = anchor.selectAll('.root').data(data); root.exit().remove(); root = root.enter() .append('div') .attr('class', 'root') .merge(root) // outer array let outer = root.selectAll('.outer').data(function(d) { return d.outer }); outer.exit().remove(); outer = outer.enter() .append('div') .attr('class','outer') .merge(outer); // inner array let inner = outer.selectAll('.inner').data(function(d) { return d.inner; }); // UPDATE INNER inner .transition() .duration(1000) .style('width', function(d) { return dr*increment.value+'px'; }); inner.exit().remove(); inner = inner.enter() .append('div') .attr("class", "inner") .style('width', function(d) { return d.r+'px'; }) .append('div') .attr("class", "innerM") .style('width', function(d) { return d.m+'px'; }) //.merge(inner); I think there might be an issue here, removing it helps, but not a complete fix // UPDATE INNER-M - Not sure if this is the best way to update let innerM = d3.selectAll('.innerM') .transition() .duration(1000) .style('width', function(d) { return dm*incrementM.value+'px'; }); // Child array let innerSArray = inner.selectAll('.innerM').data(function(d) { return ds; }) innerSArray // This doen't get updated .transition() .duration(1000) .style('width', function(d) { console.log(dr); return dr*incrementSR.value+'px'; }); innerSArray.exit().remove() innerSArray = innerSArray.enter() .append('div') .attr('class','sr') .style('width', function(d) { return d.r+'px'; }) .append('div') .attr('class','sm') .style('width', function(d) { return d.m+'px'; }) .merge(innerSArray); // UPDATE INNER-SM - Not sure if this is the best way to update let innerSM = d3.selectAll('.sm') .transition() .duration(1000) .style('width', function(d) { return dm*incrementSM.value+'px'; }); } //run once update(data); </script> </body> </html> 

I managed to solve this by splitting the double append on the .inner selection to it's own nested selection. This required wrapping .innerM 's returned data in an array literal. I was then able to continue nesting selections. I had previously tried it this way, before posting this question, but had another issue with the structure of the data. The s array wasn't being declared in every object. ie missing the s property entirely instead of being an empty array. This caused issues with the data join.

 .outer { border: 1px solid black; background-color: grey; width: 100%; height: 100px; position: relative; display: flex; } .inner { display: flex; justify-content: flex-start; border: 1px solid blue; background-color: cyan; height: 50px; position: relative; } .innerM { display: flex; border: 1px solid blue; background-color: magenta; height: 25px; position: relative; } .sr { display: flex; background-color: yellow; height: 12px; } .sm { display: flex; background-color: black; height: 6px; } 
  <!doctype html> <html> <head> <meta charset="UTF-8"> <title>#</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <script src="https://d3js.org/d3.v5.min.js"></script> <script src="https://d3js.org/d3-selection-multi.v1.min.js"></script> </head> <body> <p>Inner: <input id="increment" type="number" value="1" step="1" max="5" /></p> <p>InnerM: <input id="incrementM" type="number" value="1" step="1" max="5" /></p> <p>SR: <input id="incrementSR" type="number" value="1" step="1" max="5" /></p> <p>SM: <input id="incrementSM" type="number" value="1" step="1" max="5" /></p> <div id="anchor"></div> <script> const anchor = d3.select('#anchor'); const increment = document.getElementById('increment'); const incrementM = document.getElementById('incrementM'); const incrementSR = document.getElementById('incrementSR'); const incrementSM = document.getElementById('incrementSM'); const data = [ { "outer": [ { "inner": [ { "r": 40, "m": 10, "s": [] }, { "r": 70, "m": 13, "s": [] }, { "r": 10, "m": 15, "s": [ { "r": 20, "m": 5 } ] }, { "r": 15, "m": 9, "s": [] }, { "r": 52, "m": 20, "s": [] }, { "r": 96, "m": 30, "s": [ { "r": 50, "m": 10 } ] }, { "r": 192, "m": 60, "s": [] }, { "r": 301, "m": 50, "s": [] } ] } ] } ]; increment.addEventListener('change', function() { update(data); }); incrementM.addEventListener('change', function() { update(data); }); incrementSR.addEventListener('change', function() { update(data); }); incrementSM.addEventListener('change', function() { update(data); }); function update(data) { // main data let root = anchor.selectAll('.root').data(data); root.exit().remove(); root = root.enter() .append('div') .attr('class', 'root') .merge(root) // outer array let outer = root.selectAll('.outer').data(function(d) { return d.outer }); outer.exit().remove(); outer = outer.enter() .append('div') .attr('class','outer') .merge(outer); let inner = outer.selectAll('.inner').data(function(d) { return d.inner; }); inner .transition() .duration(1000) .style('background-color','green') .style('width', function(d) { return dr*increment.value+'px'; }); inner.exit().remove(); inner = inner.enter() .append('div') .attr("class", "inner") .style('width', function(d) { return d.r+'px'; }) .merge(inner); let innerM = inner.selectAll('.innerM').data(function(d) { return [d]; }); innerM .transition() .duration(1000) .style('width', function(d) { return dm*incrementM.value+'px'; }); innerM.exit().remove(); innerM = innerM.enter() .append('div') .attr("class", "innerM") .style('width', function(d) { return d.m+'px'; }) .merge(innerM) let sr = innerM.selectAll('.sr').data(function(d) { return ds; }); sr .transition() .duration(1000) .style('width', function(d) { return dr*incrementSR.value+'px'; }); sr.exit().remove(); sr = sr.enter() .append('div') .attr("class", "sr") .style('width', function(d) { return d.r+'px'; }) .merge(sr) let sm = sr.selectAll('.sm').data(function(d) { return [d]; }); sm .transition() .duration(1000) .style('width', function(d) { return dm*incrementSM.value+'px'; }); sm.exit().remove(); sm = sm.enter() .append('div') .attr("class", "sm") .style('width', function(d) { return d.m+'px'; }) } //run once update(data); </script> </body> </html> 

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