DEV Community

Cover image for Web-Components #102 - 5 more lessons after learning Web Components #101
Danny Engelman
Danny Engelman

Posted on • Updated on

Web-Components #102 - 5 more lessons after learning Web Components #101

Standing on the shoulders of giants

I learned as much as I could on Web Components development over the past 5 years. And I am still learning.

Credit where credit is due!

I could not have gotten this far without the answers on StackOverflow, the discussions on GitHub and the blogs from early-adoptersΒΉ.

ΒΉ) Old blogs can refer to V0 Web Component technologies not available in the current V1 standard


5 lessons to make your Web Component code better

  • Most (early) examples showed all three Web Component technologies in one code example

    • templates
    • shadowDOM
    • custom Elements API

Each can be used without the other:

  • πŸ“ You can use templates for any block of inert HTML

  • πŸ“ You can assign shadowDOM to regular HTML Elements

  • πŸ“ You can create Custom Elements without templates or shadowDOM


πŸ¦„ 1. <template>

  • creates inert HTML snippets, where previously you would have used:
  <script type="application/html">...</script>
    or
  <div style="display:none">...</div>
Enter fullscreen mode Exit fullscreen mode
  • Templates are parsed when you use template.content.cloneNode(true)

  • If you use template content once, you can skip the .cloneNode(true) part

  • Do not use a <template> just because most (early) blogs show:

  let template = document.createElement("template");
  template.innerHTML = ` CSS & HTML CONTENT `;
  this.shadowRoot.innerHTML = template.innerHTML;
Enter fullscreen mode Exit fullscreen mode

This is a very expensive way of writing:

  this.shadowRoot.innerHTML = ` CSS & HTML CONTENT `;
Enter fullscreen mode Exit fullscreen mode
  • My personal preference, when I do use/need Templates, is to keep <template>s in the <head> of the document.

    • They load early
    • My IDE does all syntax highlighting
    • I use id="UPPERCASE" so they stand out, and the <my-element> can have a generic statement: document.getElementById(this.nodeName) to read the <template>
    
      <template id="MY-ELEMENT">
        <style>
          :host { ... }
        </style>
        <div><slot><slot></div>
      </template>
    
    

    Use this.localeName for id="lowercase"


πŸ¦„ 2. super()

  • super() sets and returns the this scope.
  constructor() {
    super();
    this.title = "Web Components 102";
  }
Enter fullscreen mode Exit fullscreen mode

can be written as:

  constructor() {
    super().title = "Web Components 102";
  }
Enter fullscreen mode Exit fullscreen mode

πŸ¦„ 3. always call super() first in constructor

  constructor() {
    // Always call super first in constructor
    super();
    // Element functionality written in here
  }
Enter fullscreen mode Exit fullscreen mode

What they meant to say was:

  constructor() {
    // You can *not* reference 'this' *before* it is created by super();
    // It is valid to use *any other* JavaScript *before* super()
    const template = () => document.getElementById(this.nodeName).content.cloneNode(true);
    super().append( template() );
  }
Enter fullscreen mode Exit fullscreen mode

‼️ Note: template() is a function, it is called after super() created the 'this' scope. So this.nodeName works


πŸ¦„ 4. attachShadow

  • attachShadow sets and returns this.shadowRoot

So there is no need to create your own property:

    let shadow = this.attachShadow({mode:"open"});
Enter fullscreen mode Exit fullscreen mode
  constructor(){
      super();
      this.attachShadow({mode:"open"});
      this.innerHTML = `...`;
  }
Enter fullscreen mode Exit fullscreen mode

can all be chained:

  constructor(){
      super() // sets AND returns 'this'
        .attachShadow({mode:"open"}) // sets AND returns this.shadowRoot
        .innerHTML = `...`;
  }
Enter fullscreen mode Exit fullscreen mode

πŸ¦„ 5. appendChild vs. append

  • IE11 did not have the el.append(nodes) method; maybe that is why so many developers stick to appendChild

  • el.appendChild(element) - MDN appendChild documentation

    appends one element, and returns the element reference

  • el.append(nodes) - MDN append documentation

    appends all nodes (text nodes & elements), and returns nothing

    append does not parse HTML, like .innerHTML and .insertAdjacentHTML do

  • When you do not need the appendChild return value; you can rewrite:

    
      const shadow = this.attachShadow({mode: 'open'});
      const div = document.createElement('div');
      const style = document.createElement('style');
      shadow.appendChild(style);
      shadow.appendChild(div);
    

    to:

    
      this.attachShadow({mode: 'open'})
          .append( 
                  document.createElement('div')  ,
                  document.createElement('style')
                 );
    



Discussion (0)