How to avoid .lower() to reshuffle elements in d3?

Viewed 43

I have following d3 code that I use to draw bottom layer below all other elements of the chart:

const rectsEnter = vis.g.selectAll('rect.bottomLayer').data(data).enter()
const rectsUpdate = vis.g.selectAll('rect.bottomLayer').data(data)
vis.g.selectAll('rect.bottomLayer').data(data).exit().remove()

rectsEnter.append('rect')
            .attr('class', 'bottomLayer')
            .attr('fill', 'grey')
            .attr('x', x)
            .attr('y', (d, i) => i*size)
            .attr('width', 0)
            .attr('height', height)
            .lower()
            .transition()
            .attr('width', newWidth)

rectsUpdate
            .lower()
            .transition()
            .attr('x', x)
            .attr('y', (d, i) => i*size)
            .attr('width', width)
            .attr('height', height)
            .attr('fill', newColor)

The problem is that d3 reshuffles those rectangles on enter or update of any other element of the chart. It seems that if applied to an array (where supplied data consists or more than 1 element), d3 takes all elements that were on top and places them to the bottom.

Is there any way to avoid this reshuffling if data array contains many elements?

PS. I am trying to imitate z-index property for svg elements. Apparently, there is no z-index for svg, therefore some sacrifices have to be made to achieve desired functionality.

1 Answers

The code sample seems to be overcomplicated. Here is a more proper use of the D3 enter/exit pattern (works for D3 V4 and later):

// Create selection and apply the data (each item of data should have a unique identifier)
const rects = vis.g.selectAll('rect.bottomLayer').data(data, d => d.id);

// Enter 
const entered = rects.enter().append('rect').classed('bottomLayer', true)...

// Merge new elements with already created and update ...
const updated = entered.merge(rects);
updated.transition()...

// Exit 
rects.exit().remove();
Related