Can I use an arrow function as the callback for an event listener in JavaScript?

Arrow functions
JavaScript ES6 introduced the concept of arrow functions, a new way to define and write functions. While they might seem like a syntactic sugar on top of regular functions, they have a key difference which lies in the way the this
context is bound. I will not go into a lot of detail in this article, however I strongly suggest you read Understanding the "this" keyword in JavaScript before continuing. To summarize what the aforementioned blog post explains in more detail:
Arrow functions do not have their own bindings forthis
, resulting inthis
retaining the value of the enclosing lexical context'sthis
.
Event listener callbacks
One task that we often perform when writing browser-side JavaScript is creating event listeners. For example:
const toggleElements = document.querySelectorAll('.toggle');
toggleElements.forEach(el => {
el.addEventListener('click', function() {
this.classList.toggle('active');
});
});
In the example above, we use NodeList.prototype.forEach()
to iterate over the nodes matching a given selector and EventTarget.addEventListener()
with a regular function as the callback for the 'click'
event to swap between an active and inactive state for the clicked element. As we are using a regular function, the this
context inside the callback will be bound to the element on which the event was fired.
Arrow functions as callbacks
As we have already explained, arrow functions do not have their own bindings for this
. So what happens if we convert the previous code snippet's callback to an arrow function? Its this
context refers to the global one, which in this case is the window
object.
const toggleElements = document.querySelectorAll('.toggle');
toggleElements.forEach(el => {
el.addEventListener('click', () => {
this.classList.toggle('active'); // `this` refers to `window`
// Error: Cannot read property 'toggle' of undefined
});
});
This code will throw an error anytime the matching element is clicked, firing the event listener and executing the callback, due to the window
object not having a classList
property. Oftentimes, however, the code could fail silently as it might for example check for a condition that always evaluates to false
for window
while it should evaluate to true
for a given element, resulting in many headaches and wasted hours until the issue is discovered and fixed.
To deal with this, one could simply use the first argument of the callback function and Event.target
or Event.currentTarget
depending on their needs:
const toggleElements = document.querySelectorAll('.toggle');
toggleElements.forEach(el => {
el.addEventListener('click', (e) => {
e.currentTarget.classList.toggle('active'); // works correctly
});
});
Image credit: Matthew Smith on Unsplash
Recommended snippets
Learn the difference between cookies, local storage and session storage and start using the correct option for your needs.
If you need to check if Caps Lock is on when the user is typing in the browser, JavaScript's got you covered.
Learn how to attach an event handler to events that is executed at most once in this JavaScript blog post.