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
- 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”);