idiomatic update of group elements

http://stackoverflow.com/questions/26310664/incorrect-svg-group-being-deleted#26422833

by Nivaldo

HTML

<div id="data-field"></div>
<button id="add-btn">Add Data</button>
<p>Click on a bar to remove it</p>

CSS

.chart rect {
    fill: teal;
}
.chart text {
    fill: white;
    font: 30px sans-serif;
    text-anchor: end;
}

JavaScript

var barHeight = 75, margin = 3, padding = 3, i=0;

var chartData = [6,12,15,21,29,41];

var chart = d3.select("body")
    .append("svg")
    .attr("class","chart")
    .attr("width", 600)
    .attr("height", 600);

drawChart();

function drawChart() {
    var group = chart.selectAll("g")
        .data(chartData);
    
    // remove bars and labels associated to deleted data point
    group.exit()
        .remove();
    
    // add group element
    var groupEnter = group.enter()
        .append("g");
    
    // add rect to group element
    groupEnter
        .append("rect");
    
    // add label to group element
    groupEnter
        .append("text");
    
    // update transform and click event at the group level
    // this way BOTH bars and labels are position correctly and
    // BOTH will respond to click events
    group
        .attr("transform", function (d, i) { return "translate(0," + i * (barHeight + margin) + ")"; })
        .on("click", function (d, i) { 
            // remove data point; no need to remove corresponding DOM element; this will be taken care off via group.exit()
            chartData.splice(i, 1); 
            drawChart(); 
        });      
    
    // update bars
    group.select("rect")
        .data(chartData) // update data bound to rects
        .attr("width", function (d) { return d * 10 + "px"; })
        .attr("height", barHeight);		
    
    // update labels
    group.select("text")
        .data(chartData) // update data bound to labels
        .attr("x", function (d) { return d * 10 - padding + "px"; })
        .attr("y", barHeight / 3 + padding + "px")
        .text(function (d) { return d; });
    
    // update list of numbers
    d3.select("#data-field").text("numbers: [" + chartData.join(", ") + "]");
}

// add more data
d3.select("#add-btn")
    .on("click", function(d) { 
        var datapt = (Math.round(Math.random() * 50) + 1);
        console.log('new data point is',datapt);
        chartData.push(datapt); 
   ...