Search code examples
javascripthtmlcustom-element

Appending a custom element inside a custom element results in hidden element


Why can I not append a custom element to another custom element? Well, I can, but the result is the child element is now hidden. I don't understand because I am not appending it to the parent's shadow DOM or something.

https://jsfiddle.net/qehoLf8k/1/

HTML

<template id="text">
  <style>
    .text {
      color:red;
    }
  </style>
  <div class="text">Hello</div>
</template>

<template id="page">
  <style>
    :host {
      border:5px solid green;
      display: block;
      margin:20px;
      height:100px;
      width:100px;
    }
  </style>
  This is a page
</template>

<div id="my-body"></div>

JS

class Text extends HTMLElement {
    constructor(){
    super();
    this.attachShadow({mode: 'open'});
    const template = document.getElementById('text');
    const node = document.importNode(template.content, true);
    this.shadowRoot.appendChild(node);
  }
}
customElements.define('my-text', Text);

class Page extends HTMLElement {
    constructor(){
    super();
    this.attachShadow({mode: 'open'});
    const template = document.getElementById('page');
    const node = document.importNode(template.content, true);
    this.shadowRoot.appendChild(node);
  }
}
customElements.define('my-page', Page);

const body = document.getElementById('my-body')
const page = document.createElement('my-page')
const text = document.createElement('my-text')

// Not working, element is hidden
page.appendChild(text)
body.appendChild(page)

// Working
//body.appendChild(text)

Result: cannot see the <my-text> inside <my-page>. My intentions would be to append any amount of <my-text> elements into the <my-page> element.

Inspector image


Solution

  • Because you're using the Shadow DOM you should use a <slot> element to indicate where the children should go in the template. Otherwise the shadow won't know what to do with the children and will not render them visually.

    <template id="page">
      <style>
        :host {
          border:5px solid green;
          display: block;
          margin:20px;
          height:100px;
          width:100px;
        }
      </style>
      <slot></slot> <!-- children go here -->
      This is a page
    </template>