Listening to events inside a Web Component (from outside the Web Component)
Earlier this week, I got an email from someone asking how to listen to events inside a Web Component from other scripts.
For example, imagine you have Web Component that validates form fields…
<validate-form>
<form action="/">
<label for="username">Username</label>
<input type="email" id="username" name="username">
<label for="password">Password</label>
<input type="password" id="password" name="password">
<button>Log In</button>
</form>
</validate-form>Somewhere else in your script, you want to detect input events inside your <form>.
JavaScript actually provides you with a way to do this already, right out of the box, using event delegation and event bubbling.
Inside your handler, the event.target property is the element that caused the event to trigger, and the .value property on it is the field’s value.
let validate = document.querySelector('validate-form');
validate.addEventListener('input', function (event) {
// The input that caused the event
let input = event.target;
// The value of the input
let value = event.target.value;
});
But… this doesn’t work once you use the Shadow DOM.
Let’s say you take the advice of JS framework evangelists and move your nice “global HTML” into the Shadow DOM where it’s “safe” and “encapsulated.”
// JavaScript
customElements.define('validate-form', class extends HTMLElement {
/**
* The class constructor object
*/
constructor () {
// Gives element access to the parent class properties
super();
// Creates a shadow root
this.root = this.attachShadow({mode: 'closed'});
// Move your HTML into the shadow DOM
this.root.innerHTML = this.innerHTML;
this.innerHTML = '';
}
});
Now, your event delegation listener never fires because the Shadow DOM is isolated from the main DOM.
To get it around it, you need to add custom events to your Web Component that outside scripts can listen to.
If it sounds silly to recreate something the browser gave you for free already in exchange for what seems like minimal to no benefit at all… it is!
This is why I say that the Shadow DOM is an antipattern. It has occasional uses. They’re generally overstated.