How to capture Shadow DOM events

Shadow DOM

Shadow DOM events behave differently to traditional events.

Under normal circumstances, when an event is captured the e.target will contain a reference to the element that actually fired the event.(e.g a button)

However, take the following Custom Element, HTML and event:

class HelloWorld extends HTMLElement {
  connectedCallback() {
    const shadowDom = this.attachShadow({ mode: "open" });
    shadowDom.innerHTML = `<button type="button">Click Here</button>`;
  }
}
window.customElements.define('hello-world', HelloWorld);
<hello-world></hello-world>
document.addEventListener("click", (e) => {
    console.log(e.target);
});

When the “Click Here” button is clicked the e.target will, by default, reference the hello-world custom element(tag) and not the actual button element that fired the event.

In order, to get the actual button clicked (which is hidden inside the Shadow DOM), we need to use an alternative method.

To do this we use the composedPath function of the event object.

Once called it returns the event’s path which is an array of the objects on which listeners will be invoked.

document.addEventListener("click", (e) => {
    const target = e.composed ? e.composedPath()[0] : e.target;
    console.log(target);
});

On line 2 in the above code, we check to see if the event is composed (allowed to propagate outside the Shadow Dom). If it is we get the first item in the composePath array which will give us a reference to the button clicked and otherwise we return the normal target to cover all scenarios.