d3js updates only once

拜拜、爱过 提交于 2019-12-13 19:18:05

问题


I have a visualization task that I need to make it done with d3.js. Here's my code.

                var w = 700;
                var h = 500;
                var offset = 100;
                var padding = 20;
                var colors = d3.scale.category10();
                var svg = d3.select("body")
                    .append("svg")
                    .attr("width", w)
                    .attr("height", h);




                var texts = function(ds,ds2){
                    var stack = d3.layout.stack();
                    stack_data = stack(ds);

                    var xScale = d3.scale.ordinal()
                        .domain(d3.range(ds[0].length))
                        .rangeRoundBands([0, w-offset], 0.50);


                    var yScale = d3.scale.linear()
                        .domain([0,             
                            d3.max(stack_data, function(d) {
                                return d3.max(d, function(d) {
                                    return d.y0 + d.y -20;
                                });
                            })
                        ])
                        .range([padding, h-50]);

                    var xAxis = d3.svg.axis()
                        .scale(xScale)
                        .orient("bottom")
                        .ticks(ds[0].length);

                    gs = svg.selectAll("g").data(stack_data);

                    for (var i5 = 0; i5 < ds.length; i5++) {
                        gs.enter()
                        .append("g")
                        .attr("class", "stacked_bars")
                        .attr("fill", function(d, i) {
                            return colors(i);
                        });

                        asd = gs.selectAll("rect").data(function(d) { return d; });

                        asd.enter().append("rect");

                        asd.transition()
                            .duration(1000)
                            .attr("x", function(d, i) {
                                return xScale(i);
                            })
                            .attr("y", function(d) {
                                return h - yScale(d.y0) - yScale(d.y);
                            })
                            .attr("height", function(d) {
                                return yScale(d.y);
                            })
                            .attr("width", xScale.rangeBand())
                            .attr("class", "rectbar");

                    };

                    gs.append("g")  // add a group element to the svg
                        .attr("class", "axis") //Assign class "axis" to group
                        .attr("transform", "translate(0," + (h - padding) + ")")  // shift the axis to bottom
                        .call(xAxis);   // call the axis function 

                    gs.exit().remove();
}


                res = dataGenerator("Europe");
                dataset = res[0];
                dataset2 = res[1];

                texts(dataset,dataset2);

            d3.select("#selector").on("change", function() {
                    cont = d3.select(this).property('value');
                    res = dataGenerator(cont)

                    dataset = res[0]
                    dataset2 = res[1]

                    //svg.selectAll(".sym").remove()

                    texts(dataset,dataset2);


                }); 

It basically gets the data and generates stacked bars. When user uses the select element on the page, it updates the data and generates new results. It successfully gets the first results and when user selects another option, it makes it happen also. But when user tries to use select part once again. It only generates bars for dataset's first item.

So, in this particular case I have countries and their data as numbers, at first load and first update it successfully shows all but when it comes to second update, it only generate bars for first country in dataset. It's been hours that I'm trying to fix this. I know I only have a little mistake but couldn't make it to solve.

Also here's the jsfiddle of the code: https://jsfiddle.net/510ep9ux/4/

Since I'm new at d3.js, I may not understand the update concept well. So, any guesses?


回答1:


I made 2 simple but crucial changes to your code.

https://jsfiddle.net/guanzo/510ep9ux/6/ From

gs = svg.selectAll("g").data(stack_data);

to

gs = svg.selectAll("g.stacked_bars").data(stack_data);

The axis is also contained in a g element, so you have to ensure you're only selecting elements that are used for your data, and not unrelated elements.

From

gs.append("g")
.attr("class", "axis") 
.attr("transform", "translate(0," + (h - padding) + ")")
.call(xAxis);

to

svg.append("g")
.attr("class", "axis") 
.attr("transform", "translate(0," + (h - padding) + ")")
.call(xAxis);

If you go into the browser inspector you'll see that you have an axis element for EVERY stacked_bars element, you only need 1 obviously. It only looks like there's 1 axis because they're absolutely positioned and stacked on top of each other.

I changed it so that the axis is appended when the svg is created, and every time new data is selected, the axis will update itself.




回答2:


Solved, using two separate functions textsInit and textsUpdate :

https://jsfiddle.net/qxqdp36x/2/

Essentially you need to separate initialization and update logic, and avoid re-creating elements when updating, that causes unintended behaviours.

Also, the variables gs and asd needs to be global to be accessible to both functions.

var textsInit = function(ds, ds2) {

    var stack = d3.layout.stack();
    stack_data = stack(ds);

    var xScale = d3.scale.ordinal()
      .domain(d3.range(ds[0].length))
      .rangeRoundBands([0, w - offset], 0.50);


    var yScale = d3.scale.linear()
      .domain([0,
        d3.max(stack_data, function(d) {
          return d3.max(d, function(d) {
            return d.y0 + d.y - 20;
          });
        })
      ])
      .range([padding, h - 50]);

    var xAxis = d3.svg.axis()
      .scale(xScale)
      .orient("bottom")
      .ticks(ds[0].length);
    gs = svg.selectAll("g").data(stack_data);

    bars = gs.enter();
    bars.append("g")
      .attr("class", "stacked_bars")
      .attr("fill", function(d, i) {
        return colors(i);
      });

    asd = gs.selectAll("rect").data(function(d) {
      return d;
    });

    asd.enter().append("rect");

    asd.transition()
      .duration(1000)
      .attr("x", function(d, i) {
        return xScale(i);
      })
      .attr("y", function(d) {
        return h - yScale(d.y0) - yScale(d.y);
      })
      .attr("height", function(d) {
        return yScale(d.y);
      })
      .attr("width", xScale.rangeBand())
      .attr("class", "rectbar");

    gs.append("g") // add a group element to the svg
      .attr("class", "axis") //Assign class "axis" to group
      .attr("transform", "translate(0," + (h - padding) + ")") // shift the axis to bottom
      .call(xAxis); // call the axis function 
  }

And:

var textsUpdate = function(ds, ds2) {
    var stack = d3.layout.stack();
    stack_data = stack(ds);

    var xScale = d3.scale.ordinal()
      .domain(d3.range(ds[0].length))
      .rangeRoundBands([0, w - offset], 0.50);

    var yScale = d3.scale.linear()
      .domain([0,
        d3.max(stack_data, function(d) {
          return d3.max(d, function(d) {
            return d.y0 + d.y - 20;
          });
        })
      ])
      .range([padding, h - 50]);

    gs.data(stack_data);
        asd = gs.selectAll("rect").data(function(d) { return d; });
      asd.transition()
        .duration(1000)
        .attr("x", function(d, i) {
          return xScale(i);
        })
        .attr("y", function(d) {
          return h - yScale(d.y0) - yScale(d.y);
        })
        .attr("height", function(d) {
          return yScale(d.y);
        })
        .attr("width", xScale.rangeBand())
        .attr("class", "rectbar");
  }

Edited to fix a small bug, updating the asd selection's data.



来源:https://stackoverflow.com/questions/36971701/d3js-updates-only-once

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!