Reputation: 12694
I'm confused about data joins.
I have an entering group element, called gEnter
, to which I append
gEnter.append("g").attr("class", "dataLabels");
dataLabels
is the container element for each data label I will make.
g
is the update selection for the original group element. I bind my data like this:
var dataLabels = g.select(".dataLabels")
.selectAll(".dataLabel")
.data(function(d) {return d;});
where d
is coming from the parent g
element. For each new data point I append a .dataLabel
, and give it a starting position 30 pixels up from the axis:
var dataLabelsEnter = dataLabels.enter()
.append("g")
.attr("class", "dataLabel")
.attr("transform", function(d, i) { return "translate("+ (xScale(d.category) + (xScale.rangeBand() / 2)) +","+(yScale(0) - 30)+")"; });
Each .dataLabel
is itself a container for two text
elements, so I append them for each new data point:
dataLabelsEnter.append("text")
.attr("class", "category")
.attr("text-anchor", "middle")
.style("font-weight", function(d, i) {
return (d.category == 'Total')
? 'bold'
: 'normal';
})
.text(function(d) {return d.category;});
dataLabelsEnter.append("text")
.attr("class", "value")
.attr("text-anchor", "middle")
.attr("transform", "translate(0,20)")
.style("font-weight", "bold")
.style("fill", function(d, i) {
return (d.count >= 0)
? '#1f77b4'
: '#BB1A03';
})
.text(function(d) {
var accounting = d3.format(",");
return (d.count >= 0)
? '+$' + accounting(d.count)
: '-$' + accounting(-d.count);
});
I then move to my update code, where things get interesting. First, I update the position of the container .dataLabel
element. This works well:
dataLabels
.transition()
.duration(duration)
.attr("transform", function(d, i) {return "translate("+ (xScale(d.category) + (xScale.rangeBand() / 2)) +","+( yScale(d3.max([d.count,0])) - 30)+")"; });
Now I want to update the values of my labels. I try this:
dataLabels
.selectAll(".value")
.text(function(d, i) {
var accounting = d3.format(",");
// return d.count;
return (d.count >= 0)
? '+$' + accounting(d.count)
: '-$' + accounting(-d.count);
});
but it doesn't work. I try rebinding the data, using a .data(function(d){return d;})
, but to no avail. No matter what I do, even if the data updates, here it's still the same as the initial draw. However, if I switch to
dataLabels
.select(".value")
.text(function(d, i) {
var accounting = d3.format(",");
// return d.count;
return (d.count >= 0)
? '+$' + accounting(d.count)
: '-$' + accounting(-d.count);
});
it works.
Can anyone explain why the latter selection gets the updated the data, but the former selection doesn't? I've read Mike Bostock's recent article on selections, but am still a little confused. I believe it has something to do with this sentence from the article:
Only selectAll has special behavior regarding grouping; select preserves the existing grouping.
Perhaps selectAll is creating new groups from each .dataLabel
element, but the data is not being bound to them? I'm just not sure.
Upvotes: 1
Views: 551
Reputation: 51829
The difference is that selection.select propagates data from parent to child, whereas selection.selectAll does not. Read the paragraph you quoted again, in Non-Grouping Operations section:
Only selectAll has special behavior regarding grouping; select preserves the existing grouping. The select method differs because there is exactly one element in the new selection for each element in the old selection. Thus, select also propagates data from parent to child, whereas selectAll does not (hence the need for a data-join)!
So, when you did the data join on dataLabels
, you’ve updated the data on the parent elements. But when you call dataLabels.selectAll(".value")
, it doesn’t propagate data, so you were getting the old child data. If you switch to dataLabels.select(".value")
, it propagates data to the selected children, so you get the new data again.
You could have propagated the data using selection.data, too, but since each label has one value element here, using selection.select is easier.
(Also, you might want to specify a key function.)
Upvotes: 2