New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pass DOM element to selection functions #2246
Comments
Related: ES6’s fat arrow syntax uses a lexical binding for |
@mminer Part of what makes this tricky is that functions are actually passed three arguments (not counting this); the third is the index in the current group. An expert feature, but sometimes an indispensable one. I'd hate to throw a DOM element between these indices. |
That's understandable. I don't suppose that adding it as the fourth argument would be acceptable? I recognize at this point though that it gets into argument-overload territory. |
If you add it as the fourth argument, then everyone has to know about |
@mgold I'd suggest pushing the 3rd argument (index in current group) back to be the 4th parameter, inserting the DOM element in between. Ordinarily I wouldn't advocate such a breaking move, but as it's not mentioned in the API docs it's likely to be an infrequently used feature on the whole. The sort of code that I'd like to be writing is similar (not using arrow functions) like:
|
Would an alternative be to use (abuse?) another JavaScript feature and make the node available as
|
Our options are:
Passing the selected node at the beginning is bad because there are many times where you don’t need the selected node. This is especially true when you want to pass in another function that operates on your data. For example, if your data is numbers, you might apply a scale to set an attribute like so: d3.selectAll("circle")
.data([0, 1, 2])
.attr("cx", d3.scale.linear()
.domain([0, 10])
.range([0, width])); If the first argument is now the selected node, you’d have to say: var x = d3.scale.linear()
.domain([0, 10])
.range([0, width]);
d3.selectAll("circle")
.data([0, 1, 2])
.attr("cx", function(node, d) { return x(d); }); // Blech! This pattern of passing a function to a selection method is also common with formatting numbers. Passing the selected node at the end also doesn’t work very well because it requires you to know how many arguments are being passed to your function. In D3 3.x, that is typically three values: In D3 4.0, the call pattern will likely change slightly. Selections in 4.0 are arbitrary hierarchies rather than limited to one level of grouping: the arguments for a flat selection are selection.each(function(d, i, p, j) {
this === selection._root[j][i]; // true!
}); Setting the selected node as a global (d3.node) is also a bit icky, but as @chrisprice points out it’s similar to how we set d3.event during an event handler. @wil93’s example would look like this: d3.selectAll(".region").each(d => {
d3.select(d3.node).style("fill", this.computeColor(…));
}); A variation on this idea is a new d3.activeSelection (or d3.selectActive?) method, in the same vein as d3.activeTransition as described in d3/d3-transition#15. It is similar to d3.selectAll(".region").each(d => {
d3.activeSelection().style("fill", this.computeColor(…));
}); (Though, note that d3.activeTransition as proposed in the other issue takes a node as argument; that would presumably go away if it could infer the active element automatically.) If we go the global route, we might also take that approach for setting the current index, and then remove those pesky |
I’ve standardized d3-selection and d3-transition on the current datum |
Currently the context of a function passed to
selection.attr
(and its sibling operations) is set to the DOM element of the selection. Things get tricky when you want to both use the outerthis
and do something with the DOM element. Of course, avar self = this;
before the function solves this.What would be more convenient is if the DOM element was available as a parameter to the function, i.e.
(d, i, element)
. This would allow you to use ES6's arrow functions, which ignore the function's context.To be clear, I'm not proposing that the context be changed to something else, only that it be possible to get the relevant DOM element without it.
Regarding backwards compatibility, adding an additional argument shouldn't break anyone's code, unless they're for some reason relying on
arguments
being a particular length (though if my assumption is mistaken and such a change would indeed be a breaking one, it should probably be left until version 4).The text was updated successfully, but these errors were encountered: