It seems that I have to define a custom element only after the HTML body has been parsed. If I define it before, the contents of the custom element are empty.
Here is an MWE:
<!DOCTYPE html>
<html lang="en">
<head>
<script>
customElements.define('test-one',
class extends HTMLElement {
constructor() {
super()
console.log(this.textContent)
}
}
)
</script>
</head>
<body>
<test-one>First.</test-one>
<test-two>Another.</test-two>
<script>
customElements.define('test-two',
class extends HTMLElement {
constructor() {
super()
console.log(this.textContent)
}
}
)
</script>
</body>
</html>
test-one
outputs ""
in the console, test-two
outputs "Another."
.
However, this seems completely unintuitive and I wasted a lot of time reading the spec, but I found no explanation for this behavior. Any ideas? Where is that specified or documented? And this isn't a Chrome issue, Firefox behaves the same.
Actually you can define a Custom Element before it is added to the DOM.
What you cannot do is accessing its content (attributes, child tree, properties) in its constructor()
, because this elements have not been parsed (as @Patrick Evans suggested).
In your example you could wait for a little time to access the textContent
property.
constructor() {
super()
setTimeout( () => console.log(this.textContent) )
}
If you still want to put the custom elements definition in the header, you can wait for the page to be loaded.
window.onload => customElements.define(...)
or, depending on what you are waiting for:
document.addEventListener( 'DOMContentLoaded', customElements.define(...) )
It's not in black and white in the specs because it's rather a consequence of the parsing proccess, but you can read in this HTML Standard section:
The element's attributes and children must not be inspected, as in the non-upgrade case none will be present, and relying on upgrades makes the element less usable.
The "non-upgrade case" is when the element is defined before it is parsed.