2
class Form extends HTMLElement {
    constructor() {
        super()

    }
    connectedCallback() {
        console.log(this)
        console.log(this.innerHTML)

    }
}

customElements.define("my-form", Form);

I'm trying to access the innerHTML now for console.log(this) if I expand it in the console innerHTML is set but when I try console.log(this.innerHTML) it logs nothing.

how come even in the connectedCallback i cant access anything inside my tags.

ultimately what I'm trying to do is

class Form extends HTMLElement {
    constructor() {
        super()
        
    }
    connectedCallback() {
        let inputCounter = 0
        for (let i of this.querySelectorAll("input")) {
            this[inputCounter] = i
            inputCounter++
        }
    }
}

customElements.define("my-form", Form);

but I cant do it because I cant access anything inside the element.

connexo
  • 53,704
  • 14
  • 91
  • 128
askrill
  • 33
  • 6

3 Answers3

3

The connectedCallback fires on the opening tag.

That means all attributes are available, but NOT its children.

Simplest method is to delay execution till the Event Loop is done, and you know all your lightDOM was parsed.

<script>
customElements.define("my-form", class extends HTMLElement {
    connectedCallback() {
      setTimeout(()=>{
        let inputCounter = 0
        this.querySelectorAll("input").forEach( (inp,idx) => {
            // this[inputCounter] = idx // this[0] = idx ???
            inputCounter++
        });
        console.log(inputCounter , "inputs");
      });
    }
});
</script>

<my-form>
  <input/>
  <input/>
  <input/>
</my-form>

Up until Spring 2021 there where issues, Mozilla was the last vendor to fix this bug. For details see: wait for Element Upgrade in connectedCallback: FireFox and Chromium differences

PS.

  • No need for an empty constructor that only does super(), the parent constructor will be called when none is defined. Same applies to all callbacks.

  • No need to define a Named Class when you only want to define an element once

Danny '365CSI' Engelman
  • 16,526
  • 2
  • 32
  • 49
0

To add to @Danny '356CSI' Engelmans answer, there is an npm package available that adds this missing parsedCallback, and instead of relying on the event loop (which might be problematic in edge cases (none of which I could tell off the top of my head)) tries to make sure children are available by using some more complex heuristics like checking if the custom element has a nextSibling (in which case it must have been fully parsed): https://github.com/WebReflection/html-parsed-element

Using a timeout instead of those complex heuristics may be preferrable in case you need to support really old browsers like IE 6-10.

If you're using npm you can simply install it using

import HTMLParsedElement from 'html-parsed-element';

Usage goes like

customElements.define(
  'custom-element',
  class extends HTMLParsedElement {
    parsedCallback() {
      this.innerHTML = 'always <strong>safe</strong>!';
      console.log(this.parsed); // always true here
    }
  }
);

// or use as a mix-in
const {withParsedCallback} = HTMLParsedElement;
customElements.define(
  'other-element',
  withParsedCallback(class extends HTMLElement {
    parsedCallback() {
      this.innerHTML = 'always <strong>safe</strong>!';
      console.log(this.parsed); // always true here
    }
  })
);

This small repository was created following a discussion over on github in which I contributed the blue print of what Andrea Giammarchi (also the author of a well-known (and well-working) custom elements polyfill) has published as HTMLParsedElement.

connexo
  • 53,704
  • 14
  • 91
  • 128
0

I have spent many hours on this problem, and here is simple solution that works well.

All you need to do is wrap connectedCallback in window.requestAnimationFrame

class Form extends HTMLElement {
  constructor() {
    super()
  }
  connectedCallback() {
    window.requestAnimationFrame(()=>{
      let inputCounter = 0

      for (let i of this.querySelectorAll("input")) {
        this[inputCounter] = i
        inputCounter++
      }
    })
  }
}

customElements.define("my-form", Form);

Using this method, you will always have all child nodes available.

Dino Reic
  • 3,586
  • 3
  • 22
  • 11