I am new to javascript and currently struggling with selecting the this object while trying to do a d3 selection. I've made the following example, with a function I'm calling, and an on mousemove event:
function changeFont() {
d3.select(this)
.attr('font-size', '2em')
}
...
.on('mousemove', function() {
var mouse = d3.mouse(this);
var xVal = mouse[0];
// this would work, but not when its called in a function
// d3.select(this)
// .attr('font-size', '2em')
// this works
d3.select(this)
.attr("opacity", 1)
// this doesnt
changeFont()
});
In my main script not shown here, I am organizing my code by writing functions that handle each of the mousemove, mouseover, etc. effects. However because of these functions, I am running into this problem where I can't do d3.select(this) inside of that mouseover function... Any thoughts on what I should be doing differently?
Should I pass this as a parameter to my changeFont() function? Or should I access this in a different way?
Thanks!
Let's see what
this
is defined as for each of your approaches:this
is set by how a function is called. D3 conveniently setsthis
to be the DOM element being manipulated by using.apply
on the function passed toselection.attr()
,selection.on()
etc. However, it doesn't do this for functions called within the function passed toselection.attr()
,selection.on()
, etc.We can see that
this
is indeed the DOM element if we logthis
in the function passed toselection.on()
. Ifthis
is not explicitly set, it will be the window (unless using strict mode, then it will be undefined). We can see in the nested function,this
is indeed the window.Altocumulus's answer and Gerardo's answer avoid the
this
issue altogether, additionally you could also passthis
as some regular argument to the function (this pattern is seen in some examples). But if you want to simply be able to copy and paste the code from the inline function to some separately defined function you can use apply, which will preservethis
as the element being modified:If you needed to pass parameters to your function along with this, you can still use apply:
However, this inline function calling other functions may just be extra work. If you are only calling a function in the inline function, then just pass the called function to
selection.on()
directly, this preservesthis
without any extra steps as d3 will apply the expected value to it (it also still gives you access to the datum and index if needed):Don't place the brackets on the function in this case, we don't want to return the result of the function, we want to use the function itself.
I've use apply (
Function.prototype.apply()
) in my examples, but you can also use call (Function.prototype.call()
), as Altocumulus notes below. The use of call is quite similar. If you aren't passing any parameters to the function and only want to preservethis
, the usage is the same:someFunction.apply(this)
/someFunction.call(this)
. But, if passing parameters, call doesn't use an array for the parameters:Although Andrew's answer might be the best fit if you take the question literally, I would like to add my two cents to it. Your real problem does not seem to be to get a hold of
this
, but to repeatedly get access to that element to apply you manipulations. Since fiddling around withthis
can be a pain in JavaScript it might be worth taking a slightly different approach by directly passing the selection instead. This will also improve performance as there is no need to re-selectthis
over and over again.First, let us slightly refactor your
changeFont()
function to accept a selection object.Note, how this makes the function more generally applicable as it does not make any assumptions about the selection passed into it. It could be your
d3.select(this)
, a selection containing multiple elements or any other D3 selection object. Additionally, you do not need to preserve the previousthis
scope.There are basically two ways of calling this function.
The obvious one will directly pass the selection as an argument when calling the function:
Fortunately, there is a more elegant way of doing it by resorting to D3's own
selection.call()
which even allows for method chaining if you need to do multiple calls on the same selection.Just for completeness, since this question has already two very good answers: you can avoid the confusion with
this
if you use the third and second arguments combined. That's something that even D3 developers forget eventually.In several D3 methods, the current DOM element is just the current index of the nodes' group. So, in the anonymous function...
...
this
is justn[i]
, which you can just pass to the other functions. The_
here corresponds to the first argument, the datum: I'm using_
just to follow the convention that shows this argument is not used.The nice thing about this approach is that you can even (for whatever reasons you have) use arrow functions:
Of course, you can't get the DOM element using
this
inside the arrow function.