Thomas Cartwright Thomas Cartwright - 10 days ago 7
Javascript Question

Updating Donut Graph Using d3.js

I've been working for a few hours now and I can't get my donut graph drawn with d3.js to update to new data.

My HTML is,

<body>
<div id="pie"></div>
<script src="pie.js"></script>
</body>


And my JS is,

var dataset = [40, 20];

var width = 460,
height = 300,
radius = Math.min(width, height) / 2;

var color = ['#000000', '#FFFFFF'];

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

function render() {
var pie = d3.pie()
.sort(null);

var arc = d3.arc()
.innerRadius(radius - 100)
.outerRadius(radius - 50);

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

render();

function update() {

dataset[0] = 100;

render();
}


Currently this draws the donut graph but whenever I call the
update()
function from the console, the dataset updates but the donut graph doesn't update on screen.

I've found some other examples for bar charts (using enter, append and exit) but I can't get them to work with my example.

Any help would be appreciated, thanks!

Answer

As you know, you are only updating the data, which will not change the chart. You also know that, for changing the chart, you'll have to set an "update" selection.

Right now, you just have an "enter" selection:

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

So, every time you change the dataset, nothing happens in your chart because your "enter" selection is empty. You could just select something that doesn't exist:

var path = svg.selectAll(".foo")

But this is a terrible solution, because you'll have a bunch of paths piling up in your SVG.

So, the best solution is creating an "enter" and an "update" selection:

//this binds the data:
var path = svg.selectAll("path")
    .data(pie(data));

//this is the "enter" selection:
var pathEnter = path.enter().append("path")
    .attr("fill", function(d, i) { return color[i]; })
    .attr("d", arc);

//this is the "update" selection:
var pathUpdate = path.attr("d", arc);

Check this demo:

var dataset = [40, 20];

var width = 300,
    height = 200,
    radius = 150;

var color = ['#222', '#EEE'];

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

function render(data) {
    var pie = d3.pie()
        .sort(null);

    var arc = d3.arc()
        .innerRadius(radius - 100)
        .outerRadius(radius - 50);

    var path = svg.selectAll("path")
        .data(pie(data));

    var pathEnter = path.enter().append("path")
        .attr("fill", function(d, i) {
            return color[i];
        })
        .attr("d", arc);

    var pathUpdate = path.attr("d", arc);


}

render(dataset);

setInterval(function() {
    update();
}, 2000);

function update() {

    dataset = [Math.random() * 50, Math.random() * 50];

    render(dataset);
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="pie"></div>