Let's say, we have a DOM element by the name of element
and we want to add event listeners to it. How would you do so ?
Here are two ways which can come into mind :-
const handleClick = () =>{console.log('You can remove me later safely')}
element.addEventListener('click',handleClick);
element.addEventListener('click',()=>console.log('Try and remove me noob'));
Now when it comes to removing these event listeners, it's not possible to remove the second one since it's anonymous and for first one we can just do element.removeEventListener('click',handleClick);
What if I told you there is a way and a syntax you might not be familiar with when it comes to event listeners ?
Well here it is :-
const someObj = {
handleEvent: (e)=>console.log(`I am ${e.type} event`);
}
element.addEventListener('click',someObj);
And :-
Jokes aside, it's always been there. It's just less spoken about. And I came across this when I solved this StackOverflow question and my mind was blowwwwnn !!!
Also, You can just remove the event listener like so element.removeEventListener('click',someObj);
After finding this, I thought to myself that what if I make a bare minimum Handler
class which can abstract the registration and unregistration bit and work on the same principle ?
And this is how it looks :-
class Handler {
#element
#eventMap = {}
constructor(element, eventMap) {
this.#element = element
this.register(eventMap)
}
handleEvent(e) {
this.#eventMap[e.type](e)
}
register(eventMap) {
this.#eventMap = { ...this.#eventMap, ...eventMap }
Object.keys(this.#eventMap).forEach((event) => {
this.#element.addEventListener(event, this)
})
}
unregister(event) {
this.#element.removeEventListener(event, this)
}
unregisterAll() {
Object.keys(this.#eventMap).forEach((event) => {
this.#element.removeEventListener(event, this)
})
}
}
But what made me go for a class implementation ? Well now we know that we can pass an object to add/removeEventListener
, we can have a custom Handler
class inside which this
will point to the object instance and come into use.
Let's look at a usage sample of this code :-
const handler = new Handler(element, {
click: ()=>console.log('Yo I am clicky'),
focus: ()=>console.log('FOCUS!!!'),
});
What the above does it that for element
, it registers both the anonymous functions for respective events. And if you go further to register another function for click
like so :-
handler.register({
click: () => console.log('Well I am new clicky')
});
This will override the existing click
function that we had without any worry of handling its removal and add this new anonymous function.
Now if you want to explicitly unregister
the click
function, how would you do so ?
handler.unregister('click');
So anonymous or non-anonymous, the Handler
class will ensure that for each event type, only one function is registered for the same element. But what if I want to register multiple functions for same event type for the same element ?
Well in that case, you can create another instance of Handler
class with same element
and let it be responsible for it.
It's still a new concept to me and maybe I might have derived some wrong conclusions. But I will be more than happy to know more about it. Did you know this ? If so, have you used this ? Do you not prefer it ? Any bottlenecks ? Feel free to bash that comment section 💪.
You can go through this article for more insights into handleEvent
.
Here is a codepen where you can play with this implementation :-
Top comments (1)
After developing using JS for the past 7 years, I though I knew almost everything of the language. But with this article I've learned that addEventListener takes a Object.
I also wanted to say that about the # usage for for class properties. But I've ESLint throw errors, where the browser console accepts using # for class properties.