I'm going through the JavaScript30 challenge, and in lesson 3 he's got some event listener calling a function that references the element it's called on as this
:
const inputs = document.querySelectorAll('.controls input');
function handleUpdate() {
const suffix = this.dataset.sizing || '';
document.documentElement.style.setProperty(`--${this.name}`, this.value + suffix);
}
inputs.forEach(input => input.addEventListener('change', handleUpdate));
inputs.forEach(input => input.addEventListener('mousemove', handleUpdate));
I'm trying to rewrite it with ES6 arrow function, but I can't get the this
to work right. I got a workaround using target
:
const handleUpdate = (e) => {
const that = e.target;
const newValue = `${that.value}${that.dataset.sizing || ''}`;
etc.
}
but I first tried to bind the function like that:
input.addEventListener('change', handleUpdate.bind(this));
But this
inside the function still points to window
and I don't understand why.
Is there no "right" way to bind the function to the element in this case?
addEventListener() method is a regular anonymous function expression, not an arrow function.
Summary: addEventListener can add multiple events, whereas with onclick this cannot be done. onclick can be added as an HTML attribute, whereas an addEventListener can only be added within <script> elements.
In classic function expressions, the this keyword is bound to different values based on the context in which it is called. With arrow functions however, this is lexically bound. It means that it uses this from the code that contains the arrow function.
addEventListener() is a method of a normal DOM element and . on() is a jQuery object method. As you probably know, a jQuery object can represent more than one element and when you use the . on() method you are attaching and event handler to every element in the collection.
this
is a special keyword in Javascript that refers to the executing environment of the function:
this
will be bound to the windowthis
will be bound to the DOM element that raised the eventThe bind method basically says, when you call the function, replace this with whatever my argument is. So, for example:
let a = {}
function test_this() {
return this === a;
}
test_this(); // false
test_this.bind(a)(); // true (.bind() returns the bound function so we need to call the bound function to see the result)
Additionally arrow functions are simply syntactic sugar for binding the function's this
to the current value of this
. For example,
let b = () => { /* stuff */ }
is the same as
let b = (function () { /* stuff */}).bind(this);
(basically, I know this is an oversimplication)
In the normal course of events (not using arrow functions), this
is bound to the DOM element.
When you're executing the creation of the event handler input.addEventListener('change', handleUpdate.bind(this));
you're running in the global scope (so this === window
). So you're effectively running input.addEventListener('change', handleUpdate.bind(window));
(which is the behavior you're noticing). And using the arrow function is the same thing.
If you want to replace the callback with an anonymous function you should instead do:
const handleUpdate = function (e) {
const that = e.target;
const newValue = `${that.value}${that.dataset.sizing || ''}`;
// etc.
}
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