Kreativgeist
Kreativgeist

Reputation: 87

Fire events in a web-component

I am trying to raise events out of a webcomponent, but it does.

<my-component id="xyz" bez="hallo" hello="myScript()"></my-component>
<script>
    xyz.addEventListener("hello", function(event) {
        console.log(event.detail.name);
    });
</script>

Neither the html-tag "hello" does raise the event, nor the event-listener does.

The web component looks like this:

var button=document.createElement("button");
button.innerHTML=cap;
button.addEventListener('click', () => {
    console.log("click");
        
    button.dispatchEvent(new CustomEvent("hello", {
        detail: { name: "John" }
    }));
});
    
shadow.appendChild(button);

Can anyone help me please to find the mistake? Thanks a lot.

Code-Fiddle here: https://jsfiddle.net/b43uqsLp/2/

Upvotes: 8

Views: 4637

Answers (2)

There is some information missing in the other answer

  • the button click listener is inside shadowDOM, so composed has no use
  • default listeners like click are not stopped by shadowDOM
  • !!! CustomEvents require both composed:true and bubbles:true to escape Custom Elements with 'open' shadowDOM.
  • do not attach listeners in the connectedCallback unless you are 100% certain that is what you want; the connectedCallback runs again when DOM elements are moved in the document
  • super() returns and sets the 'this', and attachShadow() returns and sets the this.shadowRoot reference, no need to use your own variables.
    run super first means you can't access 'this' before it is created; but you can run any JS you want before the super() call

This JSFiddle: https://jsfiddle.net/CustomElementsExamples/qody0u4n/

shows composed and bubbles behaviour, with extra listeners on the document

  document.addEventListener("click", (evt) => log(evt, 'document'));
  document.addEventListener("MyEvent", (evt) => log(evt, 'document'));
<my-component id=ONE></my-component>
<my-component id=TWO composed>
    <my-component id=INSIDETWO composed></my-component>
</my-component>
<my-component id=THREE composed bubbles>
    <my-component id=INSIDETHREE composed bubbles></my-component>
</my-component>

notes

  • none of the MyEvents are caught by element ONE or the document

  • The document only receives the CustomEvent("MyEvent") when both composed and bubbles are set

  • The composed event does not stop at the shadowRoot boundary! It is halted at the Custom Element boundary. In the JSfiddle there are additional DOMlisteners on my-component to demonstrate this.


Also see: https://pm.dartus.fr/blog/a-complete-guide-on-shadow-dom-and-event-propagation/

Upvotes: 5

P.S.
P.S.

Reputation: 16412

The problem occurs because of the Shadow DOM (try to inspect your component and you will see what I mean): enter image description here

Good news, it's really simple to fix - just propagate the event across the shadow DOM into the regular DOM via composed: true property in your CustomEvent's options:

button.dispatchEvent(new CustomEvent("hello", {
    detail: { name: "John" },
    composed: true // Like this
}));

Here is JSFIDDLE.

Upvotes: 4

Related Questions