susdu
susdu

Reputation: 862

Web Components: slot appears outside shadow DOM

I want to make a web component from a <select> Element. I'm trying to get the <option> tags supplied by the user to appear in the Shadow DOM.

My component:

let tmpl = document.createElement('template');
tmpl.innerHTML = `
<select placeholder="text">
    <slot name="option"></slot>
</select>
`;

class SelectBox extends HTMLElement {
  constructor() {
    super();
    if (!this.shadowRoot) {
      this.root = this.attachShadow({mode: 'open'});
      this.root.appendChild(tmpl.content.cloneNode(true));
    }
  }
}
customElements.define('select-box', SelectBox);

HTML:

<select-box>
  <option slot="option" value="text">text</option>
</select-box>

What's being rendered is an empty select box. I can see in the console that the element is empty enter image description here

Which leads me to believe I haven't grasped the process of inserting user elements into the shadow DOM.

Upvotes: 3

Views: 1722

Answers (1)

Andrea Giammarchi
Andrea Giammarchi

Reputation: 3198

It looks like the problem is the option element that cannot be assigned as slot.

However, since your template is just a select, I wonder why you are not simply extending a select instead and call it a day.

class SelectBox extends HTMLSelectElement {
  connectedCallback() {
    // in case is not fully parsed yet ...
    if (this.selectedIndex < 0)
      return setTimeout(() => this.connectedCallback());
    this.addEventListener('change', this);
    this.parentNode.insertBefore(
      document.createElement('p'),
      this.nextSibling
    );
    this.handleEvent();
  }
  handleEvent() {
    this.nextSibling.textContent =
      `${this.selectedIndex}: ${this.value}`;
  }
}

customElements.define('select-box', SelectBox, {extends: 'select'});

With above class all you need is just the DOM with your options, and you were putting options where these didn't belong anyway, just go full built-in extend.

<select is="select-box">
  <option value="first">first</option>
  <option value="second">second</option>
</select>

You can see it working live in this code pen.

The less than 1k polyfill and the rest is described in this medium post.

I know this didn't exactly solve your issue, but unless you want to wait for all browsers to fix that at least you know there is a standard/better way to extend built-ins.

Upvotes: 5

Related Questions