I am currently studying CustomHtmlElements and I have a misunderstanding about the order in which the constructors are invoked.
For example, if I have two CustomHtmlElements:
class ExForm extends HTMLElement {
constructor() {
super();
console.log(`${this.getAttribute('name')} constructor ${performance.now()}`);
//debugger;
}
connectedCallback() {
console.log(`${this.getAttribute('name')} connected ${performance.now()}`);
}
}
customElements.define("ex-form", ExForm);
class ExInput extends HTMLElement {
constructor() {
super();
console.log(`${this.getAttribute('name')} constructor ${performance.now()}`);
//debugger;
}
connectedCallback() {
console.log(`${this.getAttribute('name')} connected ${performance.now()}`);
}
}
customElements.define("ex-input", ExInput);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<ex-form name="form1">
<ex-input name="input1" ></ex-input>
</ex-form>
<ex-form name="form2">
<ex-input name="input2"></ex-input>
</ex-form>
</body>
<script src="./index.js"></script>
</html>
I was expecting that the order of constructors execution would be:
form1, input1, form2, input2
However, when I executed the code, the order was:
form1, form2, input1, input2
Can someone clarify why there is a discrepancy between the order of constructor execution and the order in which the HTML elements are rendered to the page?
It depends on when the Web Component is defined.
Your JS code in a StackOverflow Code snippet will run after the DOM is parsed, thus will upgrade the existing Custom Elements (initially parsed as HTMLUnknownElement
)
ex-form
is defined first, so all existing <ex-form>
will be upgraded first.
Then ex-input
is defined, and all existing <ex-input>
are upgraded next.
(see snippet below) If you move the declaration before the DOM is parsed, you get the order you expect:
In general: don't rely on order in the DOM, you have no control on when the user/developer loads your Web Component file.
There is customElements.whenDefined( )
if you need to check for dependencies.
<script>
class BaseClass extends HTMLElement {
constructor() {
super();
console.log(`constructor ${this.nodeName} `, this.innerHTML);
}
connectedCallback() {
console.log(`${this.nodeName} connected ${this.innerHTML}`);
}
}
customElements.define("ex-form", class extends BaseClass {});
customElements.define("ex-input", class extends BaseClass {});
</script>
<ex-form>
<ex-input></ex-input>
<ex-input></ex-input>
</ex-form>
<ex-form>
<ex-input></ex-input>
</ex-form>