Skip to content Skip to sidebar Skip to footer

D3js Updates Only Once

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;

Solution 1:

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) {
        returncolors(i);
      });

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

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

    asd.transition()
      .duration(1000)
      .attr("x", function(d, i) {
        returnxScale(i);
      })
      .attr("y", function(d) {
        return h - yScale(d.y0) - yScale(d.y);
      })
      .attr("height", function(d) {
        returnyScale(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) {
          returnxScale(i);
        })
        .attr("y", function(d) {
          return h - yScale(d.y0) - yScale(d.y);
        })
        .attr("height", function(d) {
          returnyScale(d.y);
        })
        .attr("width", xScale.rangeBand())
        .attr("class", "rectbar");
  }

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

Solution 2:

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.

Post a Comment for "D3js Updates Only Once"