How to get different Web Components to talk to each other (part 2)
Yesterday, we learned how to connect a child and parent Web Component to each other.
Today, we’re going to look at how to use custom events to provide a more decoupled experience. Let’s dig in!
🤫 Shhh! I’m working on a new workshop-style course on Web Components. I don’t even have a landing page for it yet, but if you buy my All-Access Pass, you’ll get instant access to it as soon as its ready.
Custom events
The new CustomEvent()
method creates a custom event
object that you can emit on a specific element.
Pass in the event type as an argument. You can optionally provide an object as a second argument, with details about whether or not the event bubbles
, is cancelable
, and any detail
you want to share.
Then, you run the dispatchEvent()
method on the element, passing in the event
as an argument.
// Create a new event
let event = new CustomEvent(`my-event`, {
bubbles: true,
cancelable: true,
detail: 'Hi there!'
});
// Dispatch the event
let elem = document.querySelector('#app');
elem.dispatchEvent(event);
You can then listen for the event with the Element.addEventListner()
method, just like any other event.
elem.addEventListener('my-event', function (event) {
console.log(event.detail);
});
There are a lot of different naming conventions you can use, but for library events, I like to use a {library-name}:{event-type}
pattern.
For example, I might name the event that’s emitted when the Next button is clicked in the <wc-highlight-controls>
Web Component highlight-controls:next
.
Emitting our Web Component event
In the <wc-highlight-controls>
Web Component, let’s swap out this.highlight.next()
in the handleEvent()
method with a custom event.
/**
* Handle events
* @param {Event} event The event object
*/
handleEvent (event) {
// Create a new event
let next = new CustomEvent('highlight-controls:next', {
bubbles: true
});
// Dispatch the event
this.dispatchEvent(next);
}
We can also remove this.highlight
from our constructor()
, since we no longer need it.
Back in the <wc-highlight>
Web Component, we can list for our highlight-controls:next
event inside the constructor()
. Because events bubble, we can listen directly on our custom element and capture any events that happen inside it.
/**
* Instantiate the custom element
*/
constructor () {
// ...
// Append controls
let controls = document.createElement('wc-highlight-controls');
this.append(controls);
// Listen for highlight-constrols:next events
this.addEventListener('highlight-controls:next', this);
}
Then, we’ll handle the event by running this.next()
.
/**
* Handle events
* @param {Event} event The event object
*/
handleEvent (event) {
this.next();
}
Use events generously
If I were building this out as a real Web Component, I’d probably emit a highlight:next
event inside the .next()
method for the <wc-highlight>
Web Component.
I’d include the current
and this.active
properties, and the number of total boxes
, as part of the detail
. This event could then be used by other custom Web Components a developer might create.
/**
* Skip to the next item
*/
next () {
// ...
// Update [aria-selected]
this.boxes[current].setAttribute('aria-selected', false);
this.boxes[this.active].setAttribute('aria-selected', true);
// Create a new event
let next = new CustomEvent('highlight:next', {
bubbles: true,
detail: {
current,
next: this.active,
boxes: this.boxes
}
});
// Dispatch the event
this.dispatchEvent(next);
}
For example, you could use this to create some additional UI that displays the current box number that’s highlighted, like 1 of 3.