Some things I didn't know about custom elements

Some things I didn't know about custom elements

So today I spent a few hours on stupid things but hey, at least I've learned some new tricks about custom elements, aka Web Components. Let me share them with you.

Don't forget to define the custom element

If you have a class extending HTMLElement and you try to instantiate it without defining the custom element beforehand, it will not work.

class MyElement extends HTMLElement {
  ...
}

const el = new MyElement();
// => Uncaught (in promise) TypeError: Illegal constructor

// Do this instead
customElements.define('my-element', MyElement );
const el = new MyElement();
// :-) great!

Content within slots is rendered using light dom

While checking a routing library, I was surprised that even though it was using shadow dom, everything displayed inside the router was using global styles (eg: Bootstrap).

I was really surprised! Isn't the whole point of shadow dom to encapsulate the styles? It seems it's more complex than that and this article explains it very well.

Is there a difference in appending everything to a slot in a shadow dom or just appending nodes as usual without a shadow dom? I'm not sure!

class MyElement extends HTMLElement {
  constructor() {
    super();
    const shadow = this.attachShadow({ mode: 'open' });

    const template = document.createElement('template');
    template.innerHTML = '<slot></slot>';

    const contentTemplate= document.createElement('template');
    contentTemplate.innerHTML =
      'This is a <button class="btn btn-primary">custom</button> element!';
    shadow.appendChild(template.content.cloneNode(true));
    this.appendChild(contentTemplate.content.cloneNode(true));
  }
}

whenDefined is really great

You don't always know when something is loaded or defined in Javascript. So let's say you load a lib somewhere but configure it somewhere else, it could be tricky to apply configuration because you don't always know if everything is loaded properly.

Custom elements solve this very nicely with the whenDefined callback that allows to run init or configure global options. For example...

customElements.whenDefined('my-element').then(async () => {
   MyElement.setOptions({
      ...
   });
});

That's all for today :-)