简体   繁体   中英

How to use a custom element to wrap child custom elements into a div

I'm trying to create a wrapper Custom Element that wraps its child Custom Elements into a div.

But the child elements aren't wrapped. Instead, an empty div is inserted into the wrapper element before the child elements

<script>
  class ListItem extends HTMLElement {
    constructor() {
      super();
    }

    connectedCallback() {
      this.innerHTML = "<div>ListItem</div>";
    }
  }

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

    connectedCallback() {
      this.innerHTML = `<div class="list">${this.innerHTML}</div>`;
    }
  }

  customElements.define("list-item", ListItem);
  customElements.define("my-list", List);
</script>

<my-list>
  <list-item></list-item>
  <list-item></list-item>
  <list-item></list-item>
</my-list>

This is the result:

<my-list>
  <div class="list"></div>
  <list-item><div>ListItem</div></list-item>
  <list-item><div>ListItem</div></list-item>
  <list-item><div>ListItem</div></list-item>
</my-list>

I would have expected the following:

<my-list>
  <div class="list">
    <list-item><div>ListItem</div></list-item>
    <list-item><div>ListItem</div></list-item>
    <list-item><div>ListItem</div></list-item>
  </div>
</my-list>

You can try it out here .

It's due to the parsing execution sequence. When the <my-list> tag is detected, it is created (and connected) immediately, before its children are inserted.

As a consequent ${this.innerHTML} will return an empty string in connectedCallback() .

You could wait for the children to be parsed, for example with the help of setTimeout() :

class List extends HTMLElement {
    connectedCallback() {
        setTimeout( () => 
            this.innerHTML = `<div class="list">${this.innerHTML}</div>` 
        )
    }
}

But you'd better use Shadow DOM with <slot> to insert elements of the light DOM:

class List extends HTMLElement {
    connectedCallback() {
        this.attachShadow( { mode: 'open' } )
            .innerHTML = `<div class="list"><slot></slot></div>` 
    }
}

See the example below.

 class ListItem extends HTMLElement { connectedCallback() { this.innerHTML = "<div>ListItem</div>"; } } class List extends HTMLElement { connectedCallback() { this.attachShadow( { mode: 'open' } ) .innerHTML = `<div class="list"><slot></slot></div>` } } customElements.define("list-item", ListItem); customElements.define("my-list", List); 
 <my-list> <list-item></list-item> <list-item></list-item> <list-item></list-item> </my-list> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM