Custom event with 'composed: false' still catchable outside shadow DOM in StencilJS component

153 views Asked by At

Can somebody explain me this behaviour? So, in Stencil js, i have custom event that i am emitting using @Event decorator which has {composed: false}. Why am i able to catch it outside of shadow DOM even if i explicitly set composed to false?

From Stencil documentation composed?: boolean; A Boolean value indicating whether or not the event can bubble across the boundary between the shadow DOM and the regular DOM.

component.tsx

@Event() rate: EventEmitter<number>;

private handleStarClick(starIndex: number) {
    this.rate.emit(starIndex + 1);
}

index.html

!DOCTYPE html>
<html dir="ltr" lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0" />
    <link rel="stylesheet" href="/build/web-components.css" />
    <title>Stencil Component Starter</title>

    <script type="module" src="/build/web-components.esm.js"></script>
    <script nomodule src="/build/web-components.js"></script>

    <script type="module">
      document.querySelector("my-rating-stars").texts=["Terrible", "Bad", "Okay", "Good", "Excellent"];

****** LISTENING CUSTOM EVENT ******
      document.addEventListener("rate", (e) => console.log(e))
    </script>
  </head>
  <body>
     <my-rating-stars></my-rating-stars>
  </body>
</html>

Since i am using composed set to false (it's also default behaviour), i would expect that i am able to listen to that event ONLY in scope of shadow DOM, but instead i can listen to it from outside of shadow dom.

1

There are 1 answers

2
G. Tranter On

In your example code composed is true - default is not false. But I think you are misunderstanding how Stencil event emitters work. Your example emits an event from the host which is outside the shadow root, so it reaches the document. If you had an element inside the component's shadow DOM that dispatched an event with composed false, the event would stop propagating at your component's shadow root and not be received via document.addEventListener().

For example:

render() {
    return <Host>
        <button onclick={event => event.target.dispatchEvent(
            new CustomEvent('nonComposedEvent', { composed: false, bubbles: true })
        )}>click me</button>
    </Host>;

So for a Stencil @Event({composed: false}) event to not reach document, the component would have to be inside the shadow DOM of another component.