Skip to main content Accessibility Feedback

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();
}

Here’s a demo.

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.