Search code examples
javascripthtmlweb-componentcustom-elementhtml-templates

Disadvantage of building a custom element within the constructor?


I understand the advantages of templates in terms of performance when designing custom elements, but for structures which are only used in one element I am struggling to understand the disadvantage of building the html within the constructor() of the element class definition itself.

Put another way, what is the disadvantage of doing something like this:

const myTemplate = document.createElement("template");
myTemplate.innerHTML = `<p>my text</p>`;

customElements.define( 'my-elem', class extends HTMLElement {

  constructor() {
    super();
    this.attachShadow({ mode: "open" });
    this.shadowRoot.appendChild(myTemplate.content.cloneNode(true));
  }

})

over this:

customElements.define( 'my-elem', class extends HTMLElement {

  constructor() {
    super();

    let myP = document.createElement("p");
    let myText = document.createTextNode("my text");
    myP.appendChild(myText);

    this.attachShadow({ mode: "open" });
    this.shadowRoot.appendChild(myP);
  }

})

... when the latter option both (a) preserves the advantages of using createElement, and (b) helps prevent potential encapsulation issues introduced by defining the template outside the scope of the element definition?

I'm also aware that I could build the template with createElement instead of innerHTML in the example above, but that has the potential to introduce even more variables defined outside the scope of the element definition.


Solution

  • It is a subtle difference and boils down to requirements and/or team/personal preference.

    I would do:

    customElements.define( 'my-elem', class extends HTMLElement {
      constructor() {
        let myP = document.createElement("p");
        let myText = document.createTextNode("my text");
        myP.append(myText);
        // MDN docs are wrong, you can put code *before* super
        super() // create and return 'this'
             // create and return this.shadowRoot
            .attachShadow({ mode: "open" }) 
            .append(myP);
      }
    })
    customElements.define( 'my-elem2', class extends HTMLElement {
      constructor() {
        super()
            .attachShadow({ mode: "open" })
            .innerHTML = "Hello! Component";
      }
    })
    <my-elem></my-elem>
    <my-elem2></my-elem2>