John Jones
John Jones

Reputation: 1

d3js update redraw stacked bar graph

I am trying to make a stacked bar graph through d3js and have it update when new data is passed through an update function. I call this update function to initially call the graph and it works fine. However, when I change the data and call it again, it erases all the "rect" elements from the graph (When I console log the data, it appears to be passing through). How can I make the graph be redrawn appropriately? I have tried experimenting with the .remove() statement at the beginning, but without it the data doesn't pass through when the bars are redrawn.

    function update(my_data) {
    svg.selectAll(".year").remove();
    var year = svg.selectAll(".year")
          .data(my_data)
        .enter().append("g")
          .attr("class", "year")
          .attr("transform", function(d) { return "translate(" + x0(d.Year) + ",0)"; });
    var bar = year.selectAll(".bar")
          .data( function(d){ return d.locations; });
    bar
        .enter().append("rect")
          .attr("class", "bar")
          .attr("width", x0.rangeBand())
          .attr("y", function(d) { return y(d.y1); })
          .attr("height", function(d) { return y(d.y0) - y(d.y1); })
          .style("fill", function(d) { return color(d.name); });
}
update(data); 

Upvotes: 0

Views: 215

Answers (1)

reblace
reblace

Reputation: 4195

It's hard to tell exactly what you're doing cause your question doesn't include the data or the DOM. It would help if you included a link to a work-in-progress jsFiddle or something.

If I had to guess what's going wrong, it looks like you're doing a nested join where each year gets bound to a g element and then each location gets bound to a rect inside each g element.

The issue is likely you are only specifying the enter behavior, but not the update behavior or the exit behavior. As a result, when you try to redraw, nothing updates and nothing exits - but new data elements will get added.

It would seem that is why you have to add the selectAll().remove() to get anything to redraw. By removing everything, all the data elements will trigger the enter condition and get added again.

Take a look at these tutorials to better understand how the enter/update/exit pattern works and how nested joins work.

General Update Pattern: https://bl.ocks.org/mbostock/3808218

Nested Selections: https://bost.ocks.org/mike/nest/

Also, here is a jsFiddle I wrote some time ago to demonstrate how to use nested selections and the general update pattern together:

https://jsfiddle.net/reblace/bWp8L/

var series = svg.selectAll("g.row").data(data, function(d) { return d.key; });

/*
 * This section handles the "enter" for each row
 */
// Adding a g element to wrap the svg elements of each row
var seriesEnter = series.enter().append("g");
seriesEnter
    .attr("class", "row")
    .attr("transform", function(d, i){ 
        return "translate(" + margin.left + "," + (margin.top + (span*i)) + ")"; 
    })
    .attr("opacity", 0).transition().duration(200).attr("opacity", 1);

// Adding a text label for each series
seriesEnter.append("text")
    .style("text-anchor", "end")
    .attr("x", -6)
    .attr("y", boxMargin + (boxDim/2))
    .attr("dy", ".32em")
    .text(function(d){ return d.key; });

// nested selection for the rects associated with each row    
var seriesEnterRect = seriesEnter.selectAll("rect").data(function(d){ return d.values; });

// rect enter. don't need to worry about updates/exit when a row is added
seriesEnterRect.enter().append("rect")
    .attr("fill", function(d){ return colorScale(d)})
    .attr("x", function(d, i){ return i*span + boxMargin; })
    .attr("y", boxMargin)
    .attr("height", boxDim)
    .attr("width", boxDim);

/*
 * This section handles updates to each row
 */
var seriesUpdateRect = series.selectAll("rect").data(function(d){ return d.values});

// rect update (Will handle updates after enter)

// rect enter
seriesUpdateRect.enter().append("rect")
    .attr("x", function(d, i){ return i*span + boxMargin; })
    .attr("y", boxMargin)
    .attr("height", boxDim)
    .attr("width", boxDim);

// rect enter + update
seriesUpdateRect
    .attr("fill", function(d){ return colorScale(d)});

// Exit
seriesUpdateRect.exit();

/*
 * This section handles row exit
 */
series.exit()
    .attr("opacity", 1)
    .transition().duration(200).attr("opacity", 0)
        .remove();

Upvotes: 0

Related Questions