Update graph in D3

Some days ago I spend more than an hour to figure out how to update the graph when data change.

My goals is to update the position of element when data change.

For example, for each point A, B, C .. we have position X,Y.
If the position change I need to update the graph.

Creating the graph

To create this graph is pretty simple, we define some data..

const data = [
{“id”: “A”, “x”: 2000, “y”: 2000 },
{“id”: “B”, “x”: 5000, “y”: 5000 },
{“id”: “C”, “x”: 10000, “y”: 10000 }
]
const letters = “DEFGHIJKLMNOPQRSTUVWXYZ”.split(‘’)

Define the domain and scales..

const el = document.getElementById(“matrix”)
const SIZE = {width: el.offsetWidth, height: el.offsetHeight};
const DOMAIN = {x: [20000, 0], y: [0, 20000]};
const SCALES = {
x: d3.scaleLinear().range([SIZE.width, 0]).domain(DOMAIN.x),
y: d3.scaleLinear().range([0, SIZE.height]).domain(DOMAIN.y)
}

And then we are ready to populate our graph..

const dot = svg
.selectAll(‘.dot’)
.data(data)

dot.exit().remove()

dot.select(‘circle’)
.attr(‘cx’, d => SCALES.x(d.x))
.attr(‘cy’, d => SCALES.y(d.y))
dot.select(‘text’)
.attr(‘x’, d => SCALES.x(d.x))
.attr(‘y’, d => SCALES.y(d.y))

let dotG = dot.enter()
.append(“g”)
.attr(‘class’, ‘dot’)

dotG
.append(“circle”)
.attr(‘class’, ‘circle’)
.attr(‘fill’, ‘black’)
.attr(‘r’, ‘9’)
.attr(‘cx’, d => SCALES.x(d.x))
.attr(‘cy’, d => SCALES.y(d.y))

dotG
.append(“text”)
.attr(‘class’, ‘letter’)
.attr(‘text-anchor’, ‘middle’)
.attr(‘fill’, ‘white’)
.attr(‘alignment-baseline’, ‘central’)
.text(d => d.id)
.attr(‘x’, d => SCALES.x(d.x))
.attr(‘y’, d => SCALES.y(d.y))

But whats happens if we need to update the point position when data update?

Updating graph when data change

To archive that we need to understand two things in D3

  1. In DOM element we can bind and trigger events
$(el).bind(“updateD3”, function() { 
console.log('Updating …');
})
$(el).trigger("updateD3"); // Updating ...

This will help us because when we receive data update we simple call $(el.trigger('updateD3'))

2. enter, remove will add/remove elements that aren’t in the data.

const dot = svg
.selectAll(‘.dot’)
.data(data)

dot.exit()
.remove() // Remove elements that are not in data
dot
.enter() // Return elements that are in data but not in svg

We can use this both techniques to update our graph.

Putting all togheder

First lets move our graph definition to our event callbac

$(el).bind(“updateD3”, function () {

const dot = svg.selectAll(‘.dot’).data(data)

dot.exit().remove() // Remove elements that are not in data

dot.select(‘circle’)
.attr(‘cx’, d => SCALES.x(d.x))
.attr(‘cy’, d => SCALES.y(d.y))
dot.select(‘text’)
.attr(‘x’, d => SCALES.x(d.x))
.attr(‘y’, d => SCALES.y(d.y))

let dotG = dot.enter() // Return elements tha are in data but not in svg
.append(“g”) // add this elements in svg
.attr(‘class’, ‘dot’)

dotG
.append(“circle”)
.attr(‘class’, ‘circle’)
.attr(‘fill’, ‘black’)
.attr(‘r’, ‘9’)
.attr(‘cx’, d => SCALES.x(d.x))
.attr(‘cy’, d => SCALES.y(d.y))

dotG
.append(“text”)
.attr(‘class’, ‘letter’)
.attr(‘text-anchor’, ‘middle’)
.attr(‘fill’, ‘white’)
.attr(‘alignment-baseline’, ‘central’)
.text(d => d.id)
.attr(‘x’, d => SCALES.x(d.x))
.attr(‘y’, d => SCALES.y(d.y))

});

Now, when data change all we need to do is trigger the event again

$(el).trigger(“updateD3”);
Full source
One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.