Search code examples
javascripthtmllit-elementlit-html

LitElement slot with appendChild not working


When I tried to append slot child on my component on LitElement, It doesn't work and didn't accept to create it.

render() {
    return html `<div class="wizard-container ${this.className}"></div>`;
  }

  firstUpdated() {
    let wizardContainer = this.shadowRoot.querySelector('.wizard-container');
    for (let i = 0; i < this.steps; i++) {
      let slot = document.createElement('SLOT');
      slot.setAttribute('name', 'step_' + (i + 1))
      wizardContainer.appendChild(slot);
    };
  }

Solution

  • While I personally don't recommend you to create slots dynamically for a web component you can achieve it, you just need to keep the creation code in the render function

    For example, you can create an array out of your steps variable and iterate it using the map function to create the slots like this:

    render() {
      return html`<div class="wizard-container ${this.className}">
        ${Array.from({ length: this.steps }, (v, k) => k).map(
          item =>
            html`<slot name="step_${item}"><div>Default content ${item}</div></slot>`
        )}
      </div>`;
    }
    

    And then use your component like this:

    <my-element steps="3">
      <div slot="step_1">Custom content</div>
    </my-element>
    

    Which would result in something like:

    Default content 0 Custom content Default content 2

    Here's a live demo

    As for the reason why your previous code wasn't working like you expected, LitElement for the most part expects you to keep the code related to templating in the render function as anything you add using appendChild or similar DOM functions will get "deleted" the next time the component updates so you'd have to append it after every update by yourself

    By adding the slots directly in the render method, you guarantee they don't get deleted in unexpected ways