javascriptdom-eventsweb-componentcustom-events

Listen for global events on a Web Component


I have a main.js that makes a call to an API and receives a response object. After the response, I want to fire an event that my Custom Web Component is listening for.

makeRequest(request).then((response) => { // NOTE: the API in question returns a Promise, thus using 'then()'
   dispatchCustomEvent(response);
});

let dispatchCustomEvent = (response) => {
    console.log('dispatchCustomEvent called', response);
    let myCustomEvent = new CustomEvent('package-ready',
        {
            bubbles: true,
            composed: true,
            detail: response
        }
    );
    return document.dispatchEvent(myCustomEvent);
}

This event works in the main document. I've attached a listener to the main document to test but it is not heard on my custom component.

window.customElements.define('app-list',

    class AppList extends HTMLElement {

        constructor() {
            super();

            let shadowRoot = this.attachShadow({mode: 'open'});

            this.addEventListener('package-ready', e => console.log('package-ready heard on app-list', e.detail));
            shadowRoot.addEventListener('package-ready', e => console.log('package-ready heard on app-list Shadow Root', e.detail));
        }
}

As you can see from above, I've attached a listener both to the component (with this) and to its shadow root (for test purposes).

The event is not heard on the defined web component. I thought this might have something to do with the event capture phase (and maybe adding another flag to my custom event options object.

I'm still learning the ins and outs of Web Components and haven't figured out this piece. Any help would be appreciated!


Solution

  • You are dispatching the event on document. The event will never reach the component since events are not sent to every element on the page.

    In the capture phase the event goes from document down to the event that dispatched it, then the bubble phase walks the tree the other direction and goes from the element that dispatched it back towards document.

    Either your component needs to add its event listener to document or your code would need to change to something like this:

    makeRequest(request).then((response) => { // NOTE: the API in question returns a Promise, thus using 'then()'
       dispatchCustomEvent(response);
    });
    
    let dispatchCustomEvent = (response) => {
        console.log('dispatchCustomEvent called', response);
        let myCustomEvent = new CustomEvent('package-ready',
            {
                bubbles: true,
                composed: true,
                detail: response
            }
        );
        document.querySelectorAll('app-list').forEach(
          el => {
            return el.dispatchEvent(myCustomEvent);
          }
        );
    }

    But I really would not suggest doing that. Instead, if the event is going to be dispatched on document then you should listen for it on document.