Home > OS >  Attributes in custom element constructor missing dependent on script tag location
Attributes in custom element constructor missing dependent on script tag location

Time:02-25

I try to get a html custom element to work:

When viewing the following example in the browser I must conclude that this.dataset.text attribute in the constructor() is:

  • present before the script defining the custom element
  • empty after the script defining the custom element

<html>
    <head>
        <title>myimage</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
    <body>
        <div>
        before custom element definition: <my-element data-text="Replaced and works as expected">Not Replaced</my-element>
        </div>
        <script>
          class MyElement extends HTMLElement {
            constructor() {
              super();
              this.text_attribute= this.dataset.text;
            }

            connectedCallback() {
              if (this.text_attribute) {
                this.innerText = this.text_attribute;
              }
            }
          }
          customElements.define('my-element', MyElement);
        </script>
        <div>
        after custom element definition: <my-element data-text="Replaced">Not Replaced</my-element>
        </div>
    </body>
</html>

This seems to be a common behavior in chrome and Firefox. I can provide some more facts:

  • the this.dataset.text is always present in connectedCallback()
  • accessing the text-attribut via this.getAttribute('data-text') does behave in the same way as above

Can anyone explain the purpose of this seemingly buggy behavior?

CodePudding user response:

This isn't buggy behavior.

Your first <my-element> is processed by the DOM parser before you define it.
Thus all attributes are known (in the DOM) when the constructor executes.

In general you don't access DOM in the constructor; as it also runs when you do:

 let foo = document.createElement("my-element"); // absolutly no DOM created

Here is a trimmed down example:

<style>
  my-element { display:block }
</style>
<my-element id="FOO"> ONE </my-element>
<script>
  customElements.define('my-element', class extends HTMLElement {
    constructor() {
      super();
      document.body.append(` ► constructor id:${this.id} ◄ `);
    }
    connectedCallback() {
      document.body.append(` ► connected id:${this.id} ◄ `);
    }
  });
</script>
<my-element id="BAR"> TWO </my-element>
<my-element id="BAZ"> THREE </my-element>

NOTE

  • How FOO innerHTML is written (because it was parsed) to the DOM before its constructor runs

  • How BAR and BAZ constructor run before its innerHTML is written to the DOM
    and the id value doesn't exist yet

  • I have seen many courses where they "save" you from this standard behavior by executing the <script> async, thus after all DOM is parsed. They don't understand the technology...

  • Related