Reputation: 27
I just mimic the code d3 update pattern trying to render some rect with updated data here is my code.
function update(data){
var r = g.selectAll("rect").data(data,function(d){return (d)});
r.exit().attr("class","exit").remove()
r
.attr("class","update")
.attr("x",(d, i) =>{return i* (50+interval)})
.attr("y", (d)=>{ return y(d)})
.attr("width", "20px")
.transition(t)
.attr("height",( d => { return height-padding.top-padding.bottom-y(d);}))
r.enter()
.append("rect")
.attr("width", "20px")
.attr("class","new")
.attr("x",(d, i) =>{ return i * (50+interval)})
.attr("y", (d)=>{return y(d)})
.attr("height",( d => { return height-padding.top-padding.bottom-y(d);}))
}
then I call the update function twice
update([3,2,1,5,4,10,9,7,8,6])
setTimeout(()=>{update([2,3,1,5,4,10,9,7,8,6])},1000)
Expected: only the first and second rect will be rerendered and set class "new", but in fact, all the rect will be set class "new" .
Upvotes: 1
Views: 342
Reputation: 7210
The enter/exit pattern works when the data is an array of identified objects. Replace this code:
var r = g.selectAll("rect").data(data,function(d){return (d)});
with:
const _data = data.map((v,i) => ({id: i, value: v}));
const r = g.selectAll("rect").data(_data,d => d.id);
The D3 will identify each object and update it accordingly instead of replacing with a new one.
See it's working in a pen
UPD: If you want to highlight the items whose values have been changed, you can save the current value in an attribute of a newly added item:
r.enter()
.append("rect")
.attr('cur-value', d => d.value)
...
then, on update, query the value and compare with the one in datum:
r.attr("class","update")
...
.each(function(d) {
const rect = d3.select(this);
const prevValue = parseInt(rect.attr('cur-value'));
rect.attr('cur-value', d.value);
rect.style('fill', prevValue === d.value ? 'black' : 'red')
});
You can see it's working in the updated pen.
Upvotes: 1