D3.js Enter, Update, Exit issue

288 views Asked by At

I have a relatively simple barchart. I want to .transition() between datasets with an .on("click") event. What I'm getting is a complete redraw of an additional chart appended to the DOM id, instead of removing the original chart and transitioning or replacing it. I think I'm misunderstanding how to correctly .remove().

d3.json("data/cfilt-steps.json", function(d) {

    d.forEach(function(d) { 
        parseDate = d3.time.format("%Y-%m-%d").parse;
        d.date = parseDate(d.date); d.value = +d.value;
        });

    margin      = {top:5, right:5, bottom: 40, left:5},
    height      = 150 - margin.top - margin.bottom, 
    width       = 500 - margin.left - margin.right, 
    barPadding  = 1;

    steps    = crossfilter(d), 
    monthdim = steps.dimension(function(d){ thisDate = new Date(d.date); return thisDate.getMonth(); }),
    monthgrp = monthdim.group().reduceSum(function(d){ return d.value; });
    daydim   = steps.dimension(function(d){ thisDate = new Date(d.date); return thisDate.getDay(); }),
    daygrp   = daydim.group().reduceSum( function(d) { return d.value; });

    stepColor = d3.scale.threshold()
            .domain([100, 150, 200, 250, 300, 400, 500])
            .range(["#E3E3E3", "#D0DFD2", "#C3DABC", "#BDCB87", "#CAB44E", "#E29517", "#FF6600"]);

    d3.select("#monthly-steps-previous-selector")
            .on("click", function(d) {reDraw(monthgrp.all()) })   

    d3.select("#monthly-steps-next-selector")
            .on("click", function(d) {reDraw(daygrp.all()); })    

    function reDraw(data) {

        xScale = d3.scale.ordinal().domain(monthdim).range(0, width);

        yScale = d3.scale.linear().domain([0, d3.max(data, function(d){return d.value;})]).range([height, 5]);

        var stepbars = d3.select("#steps-bar")
            .append("svg:svg")
            .attr("width", width)
            .attr("height", height)

        stepbars.selectAll("rect")
            .data(data)
            .enter().append("rect")
            .attr("x", function(d,i){ return i * width/data.length; })
            .attr("y", function(d){ return yScale(d.value); })
            .attr("width", width/data.length - barPadding)
            .attr("height", function(d) { return height-yScale(d.value); })
            .attr("fill", function(d){ return stepColor(d.value/2000); })

    } 

reDraw(monthgrp.all());

});

Can someone show me what this is supposed to look like, or tell me what I'm doing wrong?

1

There are 1 answers

0
Hugues Stefanski On BEST ANSWER

Your reDraw function appends the svg, this means every time you call redraw a new svg is appended, hence the double chart. I would suggest to put the lines

var stepbars = d3.select("#steps-bar")
        .append("svg:svg")
        .attr("width", width)
        .attr("height", height)

above the reDraw function.

Furthermore, your redraw function does not call remove. I would do something like:

//Select and bind to data
var selection = stepbars.selectAll("rect")
        .data(data);

//Enter and create new rectangles
selection.enter()
        .append("rect");

//Update all rectangles
selection.attr("x", function(d,i){ return i * width/data.length; })
        .attr("y", function(d){ return yScale(d.value); })
        .attr("width", width/data.length - barPadding)
        .attr("height", function(d) { return height-yScale(d.value); })
        .attr("fill", function(d){ return stepColor(d.value/2000); });

//Remove unused rectangles
selection.exit().remove();