In d3, selection.select
has the side-effect of inheriting data from the parent nodes in the original selection. This is desirable in situations where data is shared between parent and child nodes so that updated data bound to the parent will get pushed to the child without requiring a data join at every level.
But what about situations where there is explicitly no relationship between the data bound to the parent and the data bound to the child? In this situation, selection.select
can be insidious because simply by selecting a node you cause that node's data to be clobbered with unrelated parent data.
What is the best technique for avoiding this? I can think of a couple of options but neither seem wonderful:
Always use selection.selectAll
everywhere except for cases where implicit data inheritance is wanted. This is not ideal, however, because it makes usage of selection.select
inconsistent with d3.select
which is simply used to select an individual node (exactly what I want to do with selection.select
).
Use d3.select
with a descendents selector instead of selection.select
to isolate a specific node. The convenient thing about using selection.select
is that it implicitly restricts the selection to descendents of the starting selection. Achieving this with the selector is not nearly as nice.
Personally, I'm not a huge fan of having a DOM-state-modifying side-effect in one particular form of some of the most commonly used functions in the API. I think I would find it easier to understand if there was an explicit call such as selection.update(selector)
to be symmetrical with selection.append
and selection.insert
.
But in the current API, I'm wondering if there some other mechanism that can be used to effectively break inheritance when using selection.select
?
I did end up submitting an issue on the D3 Github: https://github.com/mbostock/d3/issues/1443. There isn't any resolution but there is a (I think) interesting discussion of the problem. At the very bottom, Mike does offer a work-around that would work, which I'll paste here for convenience:
Not a great answer, but one way you can prevent data inheritance is to have an intermediate node without bound data.
var intermediary = selection.append("div") .datum(function() { return null; });
Then, any select from the intermediary wouldn’t propagate data from the parent selection. But of course, the intermediate node in the DOM is somewhat unfortunate.
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