Reputation: 1481
I'm using D3 to create reactive visualizations. Whenever the data changes, I find myself using this pattern a lot:
function redraw(myData) {
// if data points decreased, remove some rectangles
d3.selectAll('rect')
.data(myData)
.exit()
.remove();
// if data points increased, add some rectangles
d3.selectAll('rect')
.data(myData)
.enter()
.append('rect');
// assign attribute values to all rectangles
d3.selectAll('rect')
.data(myData)
.attr('width', (d) => d.width)
.attr('height', (d) => d.height)
.attr('fill', 'blue');
}
Is there a way to shorten this operation to a single long chain of operations?
I've seen many D3 examples like this which are fine for a one-time draw. I couldn't get them to work for redraws.
Upvotes: 2
Views: 110
Reputation: 102174
You asked...
Is there a way to shorten this operation to a single long chain of operations? (emphasis mine)
Yes, there is: if you're using D3 v5.8 or above you can take advantage of the new method selection.join()
to make a single chain with the update
, enter
and exit
selections. By the way the example you linked (made by Bostock, D3 creator) is already using join()
, so it's not "for a one-time draw" as you said.
Your whole function would be:
function redraw(myData) {
d3.select(foo).selectAll('rect')
.data(myData)
.join('rect')
.attr('width', (d) => d.width)
.attr('height', (d) => d.height)
.attr('fill', 'blue');
};
Here is a basic demo:
const svg = d3.select("svg");
d3.interval(function() {
redraw(getData());
}, 1500);
redraw(getData());
function redraw(myData) {
svg.selectAll('rect')
.data(myData)
.join('rect')
.attr('width', (d) => d.width)
.attr('height', (d) => d.height)
.attr('x', (d) => d.x)
.attr('y', (d) => d.y)
.style('fill', 'lavender')
.style('stroke', '#444');
};
function getData() {
return d3.range(~~(Math.random() * 20)).map(function(d) {
return {
x: Math.random() * 250,
y: Math.random() * 100,
width: 10 + Math.random() * 40,
height: 10 + Math.random() * 40,
}
});
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg></svg>
PS: your current function has some problems. First, d3.selectAll
is not the same of selection.selectAll
(pay attention to d3.select(foo)
in my function). Second, you don't have the correct update
, enter
and exit
selections in that function. Have a look here for the idiomatic pattern.
Upvotes: 3