简体   繁体   中英

d3 - enter - appends too many elements, as if data is not bound

I'm asking this not to get code working, but to make sure I'm understanding d3 correctly. I have created a d3 bar chart example that I'm using to better understand d3's data binding and update patterns. Fiddle is here: http://jsfiddle.net/qP9j9/22/

The fiddle explains the problem best, but I will also attempt an explanation below:

I have split out the data binding and the view into two separate functions:

var updateData = function() {

  data = generateRandomData();
  chart = d3.select('.chart').selectAll('div').data(data);
  y = d3.scale.linear().domain([0, Math.floor(d3.max(data))]).range([1,300]);

  updateChart();

};

var updateChart = function() {

  var margin = 1;
  var barWidth = (parseInt(d3.select('.chart').style('width'))/15) - margin;

  chart.transition()
        .style('height', function(d) { return y(d) + 'px' })
        .style('width', function() { return barWidth + 'px' })
        .style('left', function(d, i) { return (i * (barWidth+margin)) + 'px' });

  chart.enter().append('div')
        .style('height', function() { return 0; })
        .style('width', function() { return barWidth + 'px' })
        .style('left', function(d, i) { return (i * (barWidth+margin)) + 'px' })
        .transition()
        .style('height', function(d) { return y(d) + 'px' });

  chart.exit().remove();

};

On resize, I'm running the updateChart function. My thought there is that I'd rather not bind the data when it doesn't change, just update the view. When the data actually changes, I run updateData function.

Oddly, the updateData function needs to run twice in order for things to work correctly. If updateData is called only once (when page first loads), then when updateChart runs, the enter() function appends the data all over again. So when the page resizes, you just get duplicate dom elements.

I have researched this as much as possible, including looking at some of the "Similar Questions" that were suggested, and I can certainly "work around" the problem easily enough. But it feels like there's something fundamental I'm not understanding.

The main idea behind D3 is to link data and document, so it's a bad idea to separate the two like you've done. In particular, you're using .enter() and .exit() selections, which doesn't work properly if you haven't explicitly bound data before. In your case, the .enter() selection contains the previous selection, which is why everything is added twice.

My recommendation is two have one function that updates, not two separate ones. If you absolutely do want to separate them, you need to put handling of the .enter() and .exit() selections in the updateData() function, as I've done in your updated fiddle here .

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