Reputation: 8093
In version d3 v3, a common workflow for me was to create several svg group elements and then append a number of other child elements to each g. as an example, below I've created 3 group elements and appended a circle to each group. I then use the selection.each
method to update the radius of each circle:
var data = [2, 4, 8]
var g = d3.select('svg').selectAll('.g').data(data)
g.each(function(datum) {
var thisG = d3.select(this)
var circle = thisG.selectAll('.circle')
circle.transition().attr('r', datum * 2)
})
var enterG = g.enter().append('g').attr('class', 'g')
enterG.append('circle')
.attr('class', 'circle')
.attr('r', function(d) { return d })
g.exit().remove()
What is the proper way to do this in d3 v4? I am very confused on how best to do this. Here's an example of what i'm trying:
var data = [2, 4, 8]
var g = d3.select('svg').selectAll('.g').data(data)
g.enter()
// do stuff to the entering group
.append('g')
.attr('class', 'g')
// do stuff to the entering AND updating group
.merge(g)
// why do i need to reselect all groups here to append additional elements?
// is it because selections are now immutable?
var g = d3.select('svg').selectAll('g')
g.append('circle')
.attr('class', 'circle')
.attr('r', function(d) { return d })
// for each of the enter and updated groups, adjust the radius of the child circles
g.each(function(datum) {
var thisG = d3.select(this)
var circle = thisG.selectAll('.circle')
circle.transition().attr('r', datum * 2)
})
g.exit().remove()
Thanks in advance for any help you can provide. I've used d3 v3 for a long time and feel pretty comfortable with it. However, I am having a very hard time understanding some of the different behaviors in v4.
Upvotes: 0
Views: 455
Reputation: 1182
I think your code could be modified as follow (untested, so unsure):
var data = [2, 4, 8]
var g = d3.select('svg').selectAll('.g').data(data);
// do stuff to the entering group
var enterSelection = g.enter();
var enterG = enterSelection.append('g')
.attr('class', 'g');
//Append circles only to new elements
enterG.append('circle')
.attr('class', 'circle')
.attr('r', function(d) { return d })
// for each of the enter and updated groups, adjust the radius of the child circles
enterG.merge(g)
.select('.circle')
.transition()
.attr('r',function(d){return d*2});
g.exit().remove()
When using the first .selectAll
, only existing elements are selected. Then, by entering, you are creating new elements, that generate a new selection. When you need to update all, you simply merge the new and existing elements in a single selection.
From that selection, I simply selected all .circle
(single select - one element per g
), and then update the radius thanks to the binding API that prevents me from making a .each
call. I am unsure as how these two compares, I simply always did it this way.
Finally, here is a bl.ocks demonstrating the pattern.
Upvotes: 1