Search code examples
typescriptweb-component

web component behaves differently depending on "defer" being used or not


I implemented a WebComponent in TypeScript that looks like this and is bundled via a WebPack:

class MyTestComponent extends HTMLElement {
    // ...
    connectedCallback() {
        this.config = JSON.parse(this.innerText);
// ...
customElements.define("my-test", MyTestComponent);

In the web page it is used like this:

<my-test>
{
  "parts": [
    [3, "red"]
  ]
}
</my-test>

This worked fine using my WebPack genrated test page, but failed if I included the component into another page. I spend some time until I figured out that the defer attribute at the including script tag makes the difference. If I use defer the component works fine. If not this.innerText is empty when connectCallback is called.

I would like to understand why defer makes a difference and what's going on in detail. Can somebody explain?


Solution

  • It has nothing to do with TypeScript or WebPack. This is standard Web Components behavior.

    The connectedCallback fires on the opening tag, its inner content is not yet parsed.

    With defer you declare the Custom Element AFTER everything in the DOM is parsed

    So to make it work when your Custom Element is defined BEFORE being used,

    you have to delay execution till the Components DOM (innnerText or innerHTML) is parsed.

    Easiest is to delay till the Event Loop is empty:

    connectedCallback() {
      setTimeout(()=>{
        this.config = JSON.parse(this.innerText);
      });
    }
    

    Long answer (when Firefox fired connectedCallback late, prior to March 2021)

    BaseClass Tools, like FAST, Lit, LWC and Hybrids, shield you from this standard behavior...
    ...making you learn a Tool, not the Technology.