dbj44
dbj44

Reputation: 1998

D3: How to refresh a chart with new data?

I have created a d3 donut chart. Here is my code:

var width = 480;
var height = 480;
var radius = Math.min(width, height) / 2;
var doughnutWidth = 30;

var color = d3.scale.category10();

var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(radius - 70);

var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d[1]; });

var dataset = settings.dataset;
console.log(dataset);

var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

var path = svg.selectAll('path')
.data(pie(dataset))
.enter()
.append('path')
.attr('d', arc)
.attr('fill', function(d, i) { 
  return color(d.data[0]);
})

I have a simple form on my web page which displays a dropdown menu with several options. Every time a user changes a value on the form a new dataset is sent to my script (settings.dataset) and the donut is redrawn on the page.

Problem is, some of the values from the previous dataset remain in the DOM. In the console output below, you can see that the second dataset only has two elements. The third one is from the previous dataset. This is messing up the chart, as it is displaying a value that shouldn't be there.

enter image description here

My question: how do I clear the old values? I've read up on .exit() and .remove(), but I can't get my head around these methods.

Upvotes: 40

Views: 79188

Answers (3)

Kumar Vairakkannu
Kumar Vairakkannu

Reputation: 179

//remove and create svg
d3.select("svg").remove(); 
var svg = d3.select("body").append("svg").attr("width","960").attr("height", "600"),
inner = svg.append("g");

Upvotes: 11

CuriousSuperhero
CuriousSuperhero

Reputation: 6671

Create one function that (re)draws the pie when it's created and when it's updated.

New data should be added to pie using enter() and old data should be removed using exit().remove()

It is as simple as this:

  path.enter().append("path")
            .attr("fill", function(d, i) { return color(i); })
            .attr("d", arc)
            .each(function(d) {this._current = d;} );

  path.transition()
            .attrTween("d", arcTween);

  path.exit().remove()

Full working code -> JSFIDDLE

Upvotes: 30

DevGrowth.Tech
DevGrowth.Tech

Reputation: 1563

There are two steps to implement the 'redraw' effect you want:

First, I suppose you want the svg canvas to be drawn only once when the page is loaded for the first time, and then update the chart in the svg instead of remove and redraw the svg:

 var svg = d3.select("body")
              .selectAll("svg")
              .data([settings.dataset]);  
 // put data in an array with only one element

 // this ensures there is only one consistent svg on the page when data is updated(when the script gets executed) 
 svg.enter().append("svg")  

Second, understanding enter(), exit(), there are many great tutorials about this. In your case, I would suggest to draw the donut something like this:

var path = svg.selectAll(".donut")  
           .data(settings.data)

// bind data to elements, now you have elements belong to one of those 
// three 'states', enter, exit, or update

// for `enter` selection, append elements
path.enter().append("path").attr("d", arc).attr("fill", "teal")

// for `update` selection, which means they are already binded with data
path.transition().attrTween("d", someFunction) // apply transition here if you want some animation for data change

// for `exit` selection, they shouldn't be on the svg canvas since there is no corresponding data, you can then remove them
 path.exit().remove()

Upvotes: 12

Related Questions