1

I am trying to build a webcomponent and import it in main HTML file.

my index.html:

<!DOCTYPE html>
<html>
<head>
  <link rel="import" href="./tile.html">
</head>
<body>
  <a href="javascript:;" onclick="buildTile()">Build</a>

  <ns-tile /> <!-- Works -->

  <div id="ns-search-results"></div>
<script> 
  async function buildTile() { 
    const results = document.getElementById('ns-search-results'); 
    for(let i = 0; i < 10; i++) { 
      var nsTile = document.createElement('ns-tile'); 
      results.appendChild(nsTile); 
    } 
  }
</script>
</body>
</html>

tile.html

<template id="ns-item-tile"> 
  <style> </style> 
  <div class="tile"> Tile content </div>
</template>
<script> 
class NSTile extends HTMLElement { 
  constructor() {
    super();
  } 

  connectedCallback() { 
    var importedDoc = document.currentScript.ownerDocument; 
    let shadowRoot = this.attachShadow({ mode: 'open' }); 
    const t = importedDoc.querySelector('#ns-item-tile'); 
    const instance = t.content.cloneNode(true); 
    shadowRoot.appendChild(instance); 

    function init() { 
      const head = shadowRoot.querySelector('.tile'); 
      head.addEventListener("click", function () { 
        console.log('click'); 
      }); 
    } 
    init(); 
  } 
} // init the element

customElements.define("ns-tile", NSTile);
</script>

When I use <ns-tile /> directly in index.html, content renders properly. But when I try to render it inside a for loop in buildTile method, I am getting an error Uncaught TypeError: Cannot read property 'ownerDocument' of null at HTMLElement.connectedCallback (tile.html:16)

How can I access the html template inside tile.html so I can build using for loop?

Intervalia
  • 10,248
  • 2
  • 30
  • 60
Saravanan S
  • 1,021
  • 1
  • 10
  • 24

1 Answers1

2

A few things:

  1. Custom elements are never self closing. <ns-tile /> should be replaced with <ns-tile></ns-tile>
  2. <link rel="import" href=""> is dead. Just load the script file normally or use ES6 Import instead.
  3. If you do continue to use link rel="import" then you must move var importedDoc = document.currentScript.ownerDocument; outside of your class.
  4. I don't see anywhere that you are "defining" your component: customElements.define('tag-name', className);

Here are some changes to the component file. If you change it to a JS file then it can look like this:

const template = `<style></style> 
<div class="tile"> Tile content </div>
`;

class NSTile extends HTMLElement { 
  constructor() {
    super();
    let shadowRoot = this.attachShadow({ mode: 'open' }); 
    shadowRoot.innerHTML = template; 

    const head = shadowRoot.querySelector('.tile'); 
    head.addEventListener("click", function () { 
      console.log('click'); 
    }); 
  } 
}

customElements.define('ns-tile', NSTile);
<ns-tile></ns-tile>
Intervalia
  • 10,248
  • 2
  • 30
  • 60
  • 1
    "I don't see anywhere that you are "defining" your component" it was, but because of wrong formatting it was outside code block; just fixed that – skyboyer Dec 10 '18 at 16:20
  • Thanks @skyboyer @intevalia thanks for your code. This will work. So I assume we cannot use `template` per se for this then. I will refactor the code as you suggested. – Saravanan S Dec 10 '18 at 16:22
  • 1
    You can use ` – Intervalia Dec 10 '18 at 16:24