Either I am not using d3's selection.filter correctly, or it is buggy. I can distill the issue to a few lines. I'm in the Chrome debugger with d3 loaded. Let's start with an empty selection
d3.selectAll("nonexistant").empty()
> true
and bind some data to it.
d3.selectAll("nonexistant").data([1,2,3,4])
> [Array[4]]
Good, so it has size four. Let's check with selection.size:
d3.selectAll("nonexistant").data([1,2,3,4]).size()
> 0
Hmm, I guess that's because there are no DOM elements yet the update selection is empty since there were no previous elements. So let's make access the enter selection.
d3.selectAll("nonexistant").data([1,2,3,4]).enter()
> [Array[4]]
d3.selectAll("nonexistant").data([1,2,3,4]).enter().size()
> TypeError: undefined is not a function
d3.selectAll("nonexistant").data([1,2,3,4]).enter().append("p").size()
> 4
Not sure why the enter selection causes an error, (UPDATE: Fixed in v3.4.12) but anyway, if we try filtering using the example function in the docs,
function odds(d, i) { return i & 1; }
d3.selectAll("nonexistant").data([1,2,3,4]).filter(odds);
> [Array[0]]
d3.selectAll("nonexistant").data([1,2,3,4]).enter().filter(odds);
> []
d3.selectAll("nonexistant").data([1,2,3,4]).enter().append("p").filter(odds)
> [Array[2]]
Why is it silently filtering out all elements when there are no DOM elements bound? It does seem to be working when I already have DOM elements. But that feels pretty useless, since I don't want to create elements for data I'm discarding. Maybe if I put the filter earlier?
d3.selectAll("nonexistant").data([1,2,3,4]).filter(odds).enter().append("p").size()
> TypeError: undefined is not a function
d3.selectAll("nonexistant").data([1,2,3,4]).enter().filter(odds).append("p").size()
> TypeError: undefined is not a function
Nope. It seems the way to go is with JS's native filter on arrays:
d3.selectAll("nonexistant").data([1,2,3,4].filter(odds)).enter().append("p").size()
> 2
The d3 docs do not seem to differentiate between selections that have DOM elements bound and those that do not. It seems that I should be able to stick filter
anywhere in my method chain (and call size
on any selection), and get the correct result without a type error. Granted, filter
also supports CSS selectors that will require DOM elements, but I'm not using them here.
What I want to know: There is a mismatch between what d3 is doing and what I expect. To what extent am I harboring misconceptions about selections and what operations are valid on them? To what extent is the documentation unclear? Does any of this behavior qualify as a bug?
From the documentation of the .enter()
method:
... the entering selection only defines
append
,insert
,select
andcall
operators; you must use these operators to instantiate the entering nodes before modifying any content. (Enter selections also supportempty
to check if they are empty.)
Calling anything else doesn't produce useful results. Whether or not that's a bug, a side-effect or a feature is perhaps debatable. In almost all cases, it doesn't create any barriers, except maybe if you need to know this selection's size()
to find out how many datums from the array you pass to data()
didn't already have elements created.
Once you call append()
on the entering selection though, it behaves well, like any normal selection. In fact, append()
is returning a new selection, so it !==
the return value of enter()
.
That's when you can also check the size()
of this selection, so really it only counts as an issue if you needed to know the size PRIOR to calling append()
.
You're correct that using the native array filter
is the solution IF you don't need to even append elements where odds(d) == false
.
Filter is useful when you've already created the DOM nodes (e.g. <p>
s) that are bound to [1,2,3,4]
, and (e.g. in an event handler, when user clicks a "highlight all odds" button ) you call
d3.selectAll('p').filter(odds).css('color', 'red')
BTW, that was a really well written question.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With