Search code examples
javascriptweb-componentlit

Contents of nested slots in web component are not visible


I have a web component that should accept an arbitrary element to wrap its content in. Although I can see in Chrome Dev Tools that the slots are properly assigned, nothing appears in the DOM. Has anybody seen this issue before?

Definition

class ExampleParent extends HTMLElement {
  constructor() {
    super();
    const shadowRoot = this.attachShadow({ mode: 'open' });
    shadowRoot.innerHTML = `
      <slot name="as">
        <slot name="prefix"></slot>
        <slot></slot>
      </slot>
    `;
  }
}
customElements.define('example-parent', ExampleParent);

Invocation

<example-parent
  style="
    min-width: 100px;
    height: 40px;
    border: 1px solid red;
    display: inline-block;
  "
>
  <button slot="as"></button>
  <div slot="prefix">Prefix slot</div>
  Default
</example-parent>

Actual result

enter image description here

Expected result

enter image description here

Source code

https://stackblitz.com/edit/nested-slots-ahtfyf?file=index.html


Solution

  • You can't have nested <slot> elements kinda like you can't have nested <li> elements.

    So like with <li> where you add an extra <ul> container,
    you have to add an extra Web Component which passes on slot content:

    update: added exportparts and part to show how global CSS styles nested shadowDOMs JSWC

    class MyCoolBaseClass extends HTMLElement {
      constructor( html ) {
        let style = "<style>:host{display:inline-block;background:pink}</style>";
        super().attachShadow({mode:'open'}).innerHTML = style + html;
      }
    }
    customElements.define('el-one', class extends MyCoolBaseClass {
      constructor() {
        super(`<el-two exportparts="title"><slot name="ONE" slot="TWO"></slot></el-two>`);
      }
    });
    customElements.define('el-two', class extends MyCoolBaseClass {
      constructor() {
        super(`Hello <slot name="TWO"></slot> <b part="title">Web Components</b>`);
      }
    });
    el-one { display:block; font-size:21px }
    el-one::part(title){  color:green  }
    <el-one><b slot="ONE">Fantastic!</b></el-one>
    <el-one><b slot="ONE">Great!</b></el-one>