In d3, we may change the order of elements in a selection, for example by using raise.
Yet, when we rebind the data and use join, this order is discarded.
This does not happen when we use "the old way" of binding data, using enter and merge.
See following fiddle where you can click a circle (for example the blue one) to bring it to front. When you click "redraw", the circles go back to their original z-ordering when using join, but not when using enter and merge.
Can I achive that the circles keep their z-ordering and still use join?
const data = [{
id: 1,
v: 10,
c: 'red'
}, {
id: 2,
v: 30,
c: 'blue'
}, {
id: 3,
v: 60,
c: 'green'
}]
let nDrawCall = 0
function redraw() {
nDrawCall++
//svg1 with old enter-merge pattern that works
const circles = d3.select('#svg1')
.selectAll('circle')
.data(data, d => d.id)
circles
.enter()
.append('circle')
.on('click', function() {
d3.select(this).raise()
})
.merge(circles)
.attr('cx', d => d.v * nDrawCall)
.attr('cy', d => d.v)
.attr('r', d => d.v)
.attr('fill', d => d.c)
//svg2 with new join pattern that sadly reorders
d3.select('#svg2')
.selectAll('circle')
.data(data, d => d.id)
.join(enter => enter
.append('circle')
.on('click', function() {
d3.select(this).raise()
})
)
.attr('cx', d => d.v * nDrawCall)
.attr('cy', d => d.v)
.attr('r', d => d.v)
.attr('fill', d => d.c)
}
function reset() {
nDrawCall = 0
redraw()
}
redraw()
/*
while (true) {
iter++
console.log(iter)
sleepFor(500)
}
*/
svg {
height: 100px;
width: 100%;
}
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.3.0/d3.min.js"></script>
</head>
<body>
<button onclick="redraw()">
Redraw
</button>
<button onclick="reset()">
Reset
</button>
<div>
<svg id="svg1" />
<svg id="svg2" />
</div>
</body>
</html>