zz z
zz z

Reputation: 27

d3 General Update Pattern

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" .

Codepen

Upvotes: 1

Views: 342

Answers (1)

Michael Rovinsky
Michael Rovinsky

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

Related Questions