javascriptcomponentsslotsnative-web-component

Slots does not work on a html web component without shadow dom


I have a html web component without shadow dom and I try to add a slot. For some reason it does not work.

I expected it to switch "Foo bar" to "Hello world" but that does not happen.

  1. Does slots only works with shadow dom and a template?
  2. How can I get it to work?

class HelloWorld extends HTMLElement {
  constructor() {
    super();
  }

  connectedCallback() {
    this.innerHTML = `
      <div>
        <slot name="element-name">Foo Bar</slot>
      </div>
    `;
  }
}

customElements.define("hello-world", HelloWorld);
<hello-world>
  <span slot="element-name">Hello World</span>
</hello-world>


Solution

  • Yes, <slot> only works inside shadowDOM in a native (JSWC) Web Component

    or native HTML Element (you can attach shadowDOM to)

    Slotted content is reflected lightDOM content

    See: ::slotted CSS selector for nested children in shadowDOM slot

    A Web Component without shadowDOM only has innerHTML

    If you do this.innerHTML= on such a Web Component it replaces the innerHTML, just like on any other HTML tag

    with shadowDOM:

    <hello-world>
      <b slot="none">Mighty</b>
      <span slot="title">Web Components</span>
      Hello! 
    </hello-world>
    
    <script>
    customElements.define("hello-world", class extends HTMLElement {
      constructor() {
        super()
          .attachShadow({mode:"open"})
          .innerHTML = `<slot>default</slot>` + // default - slotting anything NOT slotted
                       `<slot name="title">Foo Bar</slot>`;
        this.onclick = (evt) => this.querySelector('b').slot = "title";
      }
    });
    </script>

    Do not fall into this one!

    You are dealing with HTML!

    <hello-world><span slot="title">Web Components rule!</span></hello-world>
    
    <hr>
    
    <hello-world><!-- comment --><span slot="notAslotname">Frameworks are cool!</span></hello-world>
    
    <hr>
    
    <hello-world>
      <span slot="title">Web Components are cool!</span>
    </hello-world>
    
    
    <script>
    customElements.define("hello-world", class extends HTMLElement {
      constructor() {
        super()
          .attachShadow({mode:"open"})
          .innerHTML = `&lt;${this.localName}> ` +
            `<style>::slotted(*){background:lightgreen}</style>` + 
            `<slot>No whitespace or linebreaks! </slot> ` + // default - slotting anything NOT slotted
            `<slot name="title">Web Components take <b>some</b> time to learn</slot>`;
      }
    });
    </script>