简体   繁体   中英

Updating D3 column chart with different values and different data sizes

Background

I am attempting to create a reusable chart object with D3.js . I have setup a chart() function that will produce a column chart. On a click event on any of the columns, the chart will update with a new random data array that will contain a random number of data points (ie the original chart could have 8 columns, but upon update, could have 20 columns or 4 columns).

Problem

Say I have 8 data points (and thus 8 columns) in my original dataset. When I update the chart with random data, the columns appropriately adjust their height to the new values - but new bars aren't added. Additionally, while the width of the columns appropriately adjust to accommodate the width of the container and the new number of data points, if that number of data points is less than the original set, then some of those columns from the original dataset will linger until the number of data points is greater than or equal than the original.

My end goal is to have new data dynamically added or old data outside of the range of the new data count dynamically removed.

I've created a jsfiddle of the behavior. You may have to click the columns a couple of times to see the behavior I'm describing. Additionally, I've pasted my code below.

Thanks in advance!

function chart(config) {

    // set default options
    var defaultOptions = {
        selector: '#chartZone',
        class: 'chart',
        id: null,
        data: [1,2,6,4, 2, 6, 7, 2],
        type: 'column', 
        width: 200,
        height: 200,
        callback: null,
        interpolate: 'monotone'
    };

    // fill in unspecified settings in the config with the defaults
    var settings = $.extend(defaultOptions, config);

    function my() { // generate chart with this function        

        var w = settings.width,
            h = settings.height,
            barPadding = 3,
            scale = 10,
            max = d3.max(settings.data);        

        var svg = d3.select(settings.selector) // create the main svg container
            .append("svg")
            .attr("width",w)
            .attr("height",h);

        var y = d3.scale.linear().range([h, 0]),
            yAxis = d3.svg.axis().scale(y).ticks(5).orient("left"),
            x = d3.scale.linear().range([w, 0]);

        y.domain([0, max]).nice();
        x.domain([0, settings.data.length - 1]).nice();        

        var rect = svg.selectAll("rect")
          .data(settings.data)
          .enter()
          .append("rect")
          .attr("x", function(d,i) {
            return i * (w / settings.data.length);
          })
          .attr("y", function(d) {
            return h - h * (d / max);
          })
          .attr("width", w / settings.data.length - barPadding)
          .attr("height", function(d) {
            return h * (d / max);
          })
          .attr("fill", "rgb(90,90,90)");

        svg.append("svg:g")
           .attr("class", "y axis")
           .attr("transform", "translate(-4,0)")
           .call(yAxis);        

        svg.on("click", function() {
            var newData = [], maxCap = Math.round(Math.random() * 100);

            for (var i = 0; i < Math.round(Math.random()*100); i++) {
                var newNumber = Math.random() * maxCap;
                newData.push(Math.round(newNumber));                
            }    

            newMax = d3.max(newData);

            y.domain([0, newMax]).nice();

            var t = svg.transition().duration(750);

            t.select(".y.axis").call(yAxis);

            rect.data(newData)
                 .transition().duration(750)
                 .attr("height", function(d) {
                    return h * (d / newMax);
                 })
                 .attr("x", function(d,i) {
                    return i * (w / newData.length);
                 })
                 .attr("width", w / newData.length - barPadding)
                 .attr("y", function(d) {
                    return h - h * (d / newMax);
                 });
            });
        }

    my();

    return my;

}

var myChart = chart();

There are actually a couple of things happening here - as you pointed out, the data outside of your new data range is not being removed, but also, new data that is outside of the existing data range is not being added. This all has to do with what selection you are modifying in your code - your update function should be using the enter() and exit() selections to handle these - see Thinking with Joins .

Here is an updated fiddle: http://jsfiddle.net/LPtTm/11/

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