Search code examples
javascripthtmlweb-component

Webcomponents based on <table>. Slot out of flow


I'm try create data-grid with "web component" based on TABLE. I'm use slot in TBODY to append new items. But TR rows rendered out of TBODY. I don't understand this behavior. shadowDom disallow use TABLE and TR elements, possibly the reason is this, but using HTMLElement.appendChild() also doesn't work

<html>
<head>
<script type="module"> 
    class DataGrid extends HTMLElement {
      TEMPLATE_ID = '#data-grid';
      constructor() {
        super();
        this._records = [];
      }
      connectedCallback() {
        let shadow = this.attachShadow({mode: 'open'});
        this._render(shadow);
      }

      _render(shadow){
        let tmpl = document.querySelector(this.TEMPLATE_ID);
        shadow.appendChild(tmpl.content.cloneNode(true));
        //this.appendChild(tmpl.content.cloneNode(true)); //
      }
    }

    customElements.define('data-grid', DataGrid);
</script>
</head>
<body>
    <template id="data-grid">
    <table border="1">
      <thead>
        <tr>
          <th>id</th>
          <th>time</th>
          <th>voltage</th>
        <tr>
      </thead>
      <tbody>
        <slot></slot>
      <tbody>
    </table>
    </template>

    <data-grid>
      <!-- need replaced to component
<grid-row time="" voltage=""> 
 -->
      <tr>
        <td>1</td>
        <td>123123123120</td>
        <td>12.0</td>
      </tr>
      <tr>
        <td>2</td>
        <td>123123133324</td>
        <td>12.1</td>
      </tr>
      <tr>
        <td>1</td>
        <td>123123122330</td>
        <td>12.2</td>
      </tr>
    </data-grid>

 </body>

Solution

  • From the <TR> documentation on MDN:

    https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tr

    Permitted parents

    <table> (only if the table has no child <tbody> element, and even then only after any <caption>, <colgroup>, and <thead> elements); otherwise, the parent must be <thead>, <tbody> or <tfoot>

    So

    <data-grid>
      <tr>
    

    is not valid HTML

    move (invalid) lightDOM to a <table> inside <data-grid> shadowDOM

    note: attachShadow() both sets and returns this.shadowRoot for free
    No need to create your own shadow variable.

    psuedo code:

    const table = this.shadowRoot.querySelector("TABLE");
    this.querySelectorAll("TR").forEach(table.appendChild);