Yesterday, I wrote about how I handle event binding in my Web Components, and it sparked a lot of great conversation on Mastodon!
A bunch of people shared their own approaches, but the one that absolutely blew my mind was the handleEvent()
method, a platform-native method for managing all of the events on your Web Component.
Let's dig in!
What is the handleEvent()
method?
The handleEvent()
method is part of the EventListener
API, and has been around for decades.
If you listen for an event with the addEventListener()
method, you can pass in an object instead of a callback function as the second argument.
As long as that object has a handleEvents()
method, the event
will be passed into it, but this
will maintain it's binding to the object.
document.addEventListener('click', { name: 'Merlin', handleEvent (event) { console.log(`The ${event.type} happened on ${this.name}.`); } });
How can you use this in a Web Component?
Let's say you have a <count-up>
component that renders a button and displays the number of times it's been clicked.
<count-up></count-up>
customElements.define('count-up', class extends HTMLElement { // Instantiate the component constructor () { // Get the parent properties super(); // Define properties this.count = 0; this.button = document.createElement('button'); // Inject the button this.append(this.button); this.render(); } // Render the UI render () { this.button.textContent = `Clicked ${this.count} times`; } });
Here's a demo (it doesn't have interactivity yet).
Using the technique I shared yesterday, here's how I would typically have added an event listener.
customElements.define('count-up', class extends HTMLElement { // Instantiate the component constructor () { // Get the parent properties super(); // Define properties this.count = 0; this.button = document.createElement('button'); this.handler = this.createHandler(); // Inject the button this.append(this.button); this.render(); } // Create the click handler createHandler () { return (event) => { this.count++; this.render(); }; } // Start listening connectedCallback () { this.button.addEventListener('click', this.handler); } // Stop listening disconnectedCallback () { this.button.removeEventListener('click', this.handler); } // Render the UI render () { this.button.textContent = `Clicked ${this.count} times`; } });
Now, let's look at how you could write this using the handlEvent()
method.
Using the handleEvent()
method in a Web Component
With this approach, we no longer need to setup a this.handler
or use arrow functions.
We pass this
, the Web Component class, directly into the addEventListener()
method, which runs the handleEvent()
method whenever the event is triggered.
customElements.define('count-up', class extends HTMLElement { // Instantiate the component constructor () { // Get the parent properties super(); // Define properties this.count = 0; this.button = document.createElement('button'); // Inject the button this.append(this.button); this.render(); } // Create the click handler handleEvent (event) { this.count++; this.render(); } // Start listening connectedCallback () { this.button.addEventListener('click', this); } // Stop listening disconnectedCallback () { this.button.removeEventListener('click', this); } // Render the UI render () { this.button.textContent = `Clicked ${this.count} times`; } });
Here's a demo of the handleEvent()
method.
As you can see, we've shaved a few steps off.
Handling multiple event types
What happens if you need to listen to multiple events in a Web Component?
customElements.define('awesome-sauce', class extends HTMLElement { // Instantiate the component constructor () { super(); } // Handle events handleEvent (event) { // ... } // Listen for events connectedCallback () { this.addEventListener('click', this); this.addEventListener('input', this); this.addEventListener('close', this); } });
You could handle all over your events inside the handleEvent()
method using some if...else
logic.
// Handle events handleEvent (event) { if (event.type === 'click') { // ... } else if (event.type === 'input') { // ... } else { // ... } }
But Andrea Giammarchi shares a more elegant technique.
You can create on*
methods in your class for each of your different event types, and use the handleEvent()
method to automatically route events to them.
// Handle events handleEvent (event) { this[`on${event.type}`](event); } // Click events onclick (event) { // ... } // Input events oninput (event) { // ... }
Why is this the best approach for events in Web Components?
I like how simple and scalable it is. It makes the code much cleaner and easier to read (in my opinion).
Andrea cites a few other reasons.
In his testing, it used substantially less memory than using arrow functions, adding additional properties (like I do), or using .bind()
(and approach that was shared with me by a few folks).
It also makes it much easier to add and remove events. You always pass this
in to the addEventListner()
and removeEventListener()
methods, and let the handleEvent()
method handle the rest.
The naming convention this enforces on your handler functions is clear and consistent.
And if you accidentally listen to the same event multiple times, with this approach, your handler function only runs once.
This is how I'll be authoring event listeners in my web components going forward.
🔥 Cyber Monday Sale (extended)! This week only, save 50% on Lean Web Club membership, private coaching, and all of my courses and workshops.
Cheers,
Chris
Want to share this with others or read it later? View it in a browser.
0 Komentar untuk "[Go Make Things] The handleEvent() method is the absolute best way to handle events in Web Components"