0

TLDR: As soon as I define() a Custom Element with Shadow DOM, the Light DOM declared within the same element disappears from the browser viewport.

Why is the Light DOM within the Custom Element no longer displaying?


I am learning / experimenting with Custom Elements and Shadow DOM.

I notice that if I have the following Custom Element without any Shadow DOM, it works:

<my-company>&copy; 2020, </my-company>

But if I then add custom content to the Custom Element via the Shadow Dom...

class myCompany_Element extends HTMLElement {

  constructor() {
    super();
    this.root = this.attachShadow({mode: "open"});
  }

  connectedCallback() {

    this.root.textContent += 'Cyberdyne Systems';
  }
}

customElements.define('my-company', myCompany_Element);
<my-company>&copy; 2020, </my-company>

the original hard-coded content disappears. (Even though it's still visible in the DOM Inspector.)

I was expecting:

© 2020, Cyberdyne Systems

What's happening here?

Does this mean it's not possible to have

  • a Custom Element with and without Shadow DOM

and that I can only have either:

  • a Custom Element without Shadow DOM
  • a Custom Element with Shadow DOM and slots

Or is it possible to have a Custom Element with and without Shadow DOM?

Rounin
  • 27,134
  • 9
  • 83
  • 108

2 Answers2

1

Here's a simple solution for your use case that doesn't require slots:

class myCompany_Element extends HTMLElement {
    constructor() {
        super()
        this.root = this.attachShadow({ mode: 'open' })
    }

    connectedCallback() {
        // added line below
        this.root.textContent += this.textContent
        this.root.textContent += 'Cyberdyne Systems'
    }
}

customElements.define('my-company', myCompany_Element)
<my-company>&copy; 2020, </my-company>

If you wanted, you could also zero out this.textContent to '' after setting this.root.textContent. As far as I can tell using Chrome dev tools, doing so makes no difference to accessibility, but might be desirable from a developer experience point of view.

Lionel Rowe
  • 5,164
  • 1
  • 14
  • 27
  • Ah. Brilliant. I like this very much. Excellent work. Thank you. – Rounin Sep 21 '20 at 14:59
  • Building on your solution, I note, upon further reading, I can use _custom attributes_ in my _custom element_: `© 2020, ` and `this.root.textContent = this.textContent + this.getAttribute('corporation')`. – Rounin Sep 21 '20 at 15:33
0

As far as I can tell from my experimentation and further reading, this is what is happening:

By invoking attachShadow() in the class definition, we explicitly determine the element will become a Shadow Host when it is define()-d:

constructor() {
  super();
  this.root = this.attachShadow({mode: "open"});
}

Once the element has officially become a Shadow Host its Light DOM will no longer display in the viewport:

  • the Shadow Host will not display Light DOM already declared in the HTML
  • the Shadow Host will not display Light DOM added later, via Javascript

Further Confirmation:

By default, if an element has shadow DOM, the shadow tree is rendered instead of the element's children

Source: https://polymer-library.polymer-project.org/3.0/docs/devguide/shadow-dom


Conclusion:

The standard (and only?) approach to bringing back the element's Light DOM children, is to use named <slot>-s, in this case:

class myCompany_Element extends HTMLElement {

  constructor() {
    super();
    this.root = this.attachShadow({mode: "open"});
  }

  connectedCallback() {
 
    this.root.innerHTML += '<slot name="template"></slot>';
    this.root.innerHTML += 'Cyberdyne Systems';
  }
}

customElements.define('my-company', myCompany_Element);
<my-company><span slot="template">&copy; 2020, </span></my-company>
Rounin
  • 27,134
  • 9
  • 83
  • 108
  • 1
    I haven't tested, but I presume you can add lightDOM content later, which is then available to shadowDOM slots. Because I know changing slotnames dynamically IN lightDOM WILL show it in s. See bottom of long :slotted post: https://stackoverflow.com/questions/61626493/slotted-css-selector-for-nested-children-in-shadowdom-slot/61631668#61631668 – Danny '365CSI' Engelman Sep 23 '20 at 08:14