javascripttypescriptcustom-elementhtml-templates

How do custom HTML tags interact with cloning a template?


Here's the test case that I'm confused with. In html (in the end of body):

<template id="test">
    <test-tag class="test-id"></test-tag>
</template>

In the script:

class TestTag extends HTMLElement {
    constructor() {
        super();
        console.log("TestTag created");
    }
    get Property() : string {
        return "Hello";
    }
}

// in some function:
    customElements.define("test-tag", TestTag);
    customElements.whenDefined("test-tag").then(() => {
        console.log("test-tag defined");
        var tag = document.createElement("test-tag") as TestTag;
        console.log(tag.Property);

        var template = document.getElementById("test") as HTMLTemplateElement;
        var clone = template.content.cloneNode(true) as DocumentFragment;
        var tag2 = clone.querySelector<TestTag>(".test-id");
        if(tag2 == null){
            console.error("tag2 is null");
        } else {
            //customElements.upgrade(tag2);
            if(!(tag2 instanceof TestTag)){
                console.error("WTF?!"); //did not expect to be here
            }
            console.log(tag2.Property); //tag2.Property is undefined
        }
    });

The first tag object works as I expect. The second one doesn't: it seems that cloning the template creates a generic DOM element rather than an instance of my TestTag class.

To me it seems to be the main function of custom elements - to define necessary behaviour and reuse it while still describing complex divs with HTML markup rather than some ugly construction code of dozens createElement/appendChild calls. So it seems like I'm missing something obvious. Like maybe shadow DOM mechanic but customElements.upgrade() does nothing in this case.

My question is: if there's some well-known part of using custom elements I've missed then what is it; if not, what can be reasonably done to obtain a TestTag instance as tag2 value in the example above and why my example behaves the way it does?


Solution

  • There is a difference between a Class Object and a DOM Element attached to the DOM

    <template id="test">
      <test-tag class="test-id"></test-tag>
    </template>
    
    <script>
      class TestTag extends HTMLElement {
        get Property() {
          return "Hello";
        }
      }
    
      customElements.define("test-tag", TestTag);
      var tag = document.createElement("test-tag");
      var template = document.getElementById("test");
      var clone = template.content.cloneNode(true);
      var tag2 = clone.querySelector(".test-id");
      console.log(tag2.nodeName, tag2.constructor.name, tag2 instanceof HTMLElement, tag2 instanceof TestTag, tag2.Property);
    
      document.body.append(tag2); // MAKE IT DOM!!!
      console.log(tag2.nodeName, tag2.constructor.name, tag2 instanceof HTMLElement, tag2 instanceof TestTag, tag2.Property);
    
    </script>