Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

234
Views
If an event issued on "this" bubbles up out of my component then why do we need the "composed" property on custom events?

So I have read several pieces that say if you want a custom event to traverse the shadow DOM boundary and cross into the light DOM you need to set the custom event's composed property to true. I noticed however that any events I dispatch from a web component's this. make it out of the shadowRoot component just fine, and ones that are dispatched from this.shadowRoot stay inside. So why do I need the "composed" property? Am I doing something wrong?

const internalEvent = new CustomEvent("internalEvent", {bubbles: true, cancelable: false})
const externalEvent = new CustomEvent("externalEvent", {bubbles: true, cancelable: false})

class MyComponent extends HTMLElement {
    constructor() {
        super()
        this.attachShadow({ mode: 'open' })
        this.shadowRoot.innerHTML = `
            <button id="internalButton">INTERNAL</button>
            <button id="externalButton">EXTERNAL</button>
        `
        this.internalButton = this.shadowRoot.getElementById("internalButton")
        this.externalButton = this.shadowRoot.getElementById("externalButton")
    }
    connectedCallback() {
        this.internalButton.addEventListener("click", ()=>{
            this.shadowRoot.dispatchEvent(internalEvent)
        })
        this.externalButton.addEventListener("click", ()=>{
            this.dispatchEvent(externalEvent)
        })
        this.shadowRoot.addEventListener("internalEvent", (event)=>{
            console.log("Internal event detected internally.")
        })
        this.shadowRoot.addEventListener("externalEvent", (event)=>{
            console.log("External event detected internally!")
        })
    }
}

document.addEventListener("internalEvent", ()=>console.log("Internal event detected externally!"))
document.addEventListener("externalEvent", ()=>console.log("External event detected externally."))
customElements.define('my-component', MyComponent)

edit: I'm just struggling to think of any reason where, to get a message to leave your component, you'd prefer to dispatch it within the shadowRoot and add a special property, rather than just dispatching it straight into the light DOM in the first place.

about 4 years ago · Juan Pablo Isaza
3 answers
Answer question

0

As others have mentioned, this is not in the shadow DOM of your component; it is the component that has this shadow DOM.

if I can just dispatch from this why would I not just do that instead of adding extra properties to my event?

It still wouldn't be able pass any possible surrounding shadowDOM boundaries (your web component may very well be a child or descendant of another web component that utilizes shadow DOM). This may be desirable or not, depending on where you want the event to be monitorable.

Also be aware that connectedCallback can be called multiple times, for example if an element is moved in the DOM; make sure to always remove any event listeners which you added in the connectedCallback in the disconnectedCallback, or even preferable, add the internal listeners in the constructor (which is guaranteed to only ever run once, and saves you the hassle of needing references to the listeners to be able to remove them).

about 4 years ago · Juan Pablo Isaza Report

0

'this' is the Custom Element/Web Component <my-component>,
'this' is not inside the elements shadowRoot.

So Events you dispatch from 'this', do not cross shadowDOM boundaries.

You only need composed: true when Events need to cross (aka "escape") shadowDOM –

<script>
  const EventName = "HelloFromComponent";
  customElements.define('my-component', class extends HTMLElement {
    constructor() {
      let attach = (btn, composed = false, el = this.shadowRoot.getElementById(btn)) =>
        el.onclick = () => {
          el.dispatchEvent(new CustomEvent(EventName, {
            bubbles: true,
            cancelable: false,
            composed: composed
          }))
        }
      super().attachShadow({mode: 'open'})
             .innerHTML = `<button id="one">One</button><button id="two">Two</button>`;
      attach("one", /* composed = */ false );
      attach("two", /* composed = */ true  );
    }
    listen(where) {
      where.addEventListener(EventName, (evt) => {
        console.log(where.nodeName, evt.type, evt.composed, );
      })
    }
    connectedCallback() {
      this.listen(this);
      this.listen(document);
    }
  });
</script>
<my-component></my-component>

about 4 years ago · Juan Pablo Isaza Report

0

It has just dawned on me that one reason (maybe The reason) would be if you wanted the event to be caught both inside AND outside of your component. Without the compose property you'd need to issue two separate events.

about 4 years ago · Juan Pablo Isaza Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!