WTF innerHTML
is an empty string?!?
<script>
customElements.define("my-component", class extends HTMLElement {
connectedCallback(){
console.log( this.innerHTML.length , " chars in innerHTML" );
}
});
</script>
<my-component>Hello Web Component World!</my-component>
HTML only example
Key is to understand when DOM was parsed
In this script below, everyone will agree the first console
logs 0, because it executed before the remaining DOM was parsed.
<script>
console.log(document.querySelectorAll("div").length , "divs");
</script>
<div></div>
<div></div>
<div></div>
<script>
console.log(document.querySelectorAll("div").length , "divs");
</script>
Logs:
0 divs
3 divs
Web Component connectedCallback
The same applies to the connectedCallback
, it fires/fired on the opening tag
So like the first <script>
tag in the first example,
all following DOM (3 DIVs in lightDOM) is NOT parsed yet
<script>
customElements.define("my-component", class extends HTMLElement {
connectedCallback() {
const LOG = () => console.log(this.id, this.children.length, "divs");
LOG();
setTimeout( () => LOG() );
}
});
</script>
<my-component id="Foo">
<div></div>
<div></div>
<div></div>
</my-component>
Logs:
Foo 0 divs
Foo 3 divs
I repeat: the connectedCallback
fires on the opening tag.
That means all attributes are available ON the Web Component,
but NOT its three <div>
child elements IN lightDOM.
Wait till lightDOM is parsed
Simplest method to get that lightDOM content is to delay execution till the Event Loop is empty again, and you know more (and most likely all) of your lightDOM was parsed.
Background knowledge:
Youtube: Jake Archibald on the web browser event loop, setTimeout, requestAnimationFrame
BUT!
Because the Event Loop can be(come) empty when the (large) DOM is still being parsed!
This gets you the next N elements parsed, not ALL elements in a (large) lightDOM!
Rough tests show around N=1000 (1000 lightDOM elements) are safe to work with.
but your mileage may vary for complex CPU consuming elements
Maybe just increase to 1 millisecond setTimeout delay
requestAnimationFrame (rAF)
requestAnimationFrame
can also be used. Read!:
setTimeout vs requestAnimationFrame
But because setTimeout
triggers later you know more N elements were parsed.
requestAnimationFrame(()=>{
// ... your code
});
Do watch Jakes video before using rAF!
Potential pitfall: the attributeChangedCallback
!!! The attributedChangedCallback
fires BEFORE the connectedCallback
for every attribute
defined as an observed attribute in static get observedAttributes()
If none of those Observed attributes exist on the Element in the DOM, attributeChangedCallback
will not execute.
get ALL children - parsedCallback()
For getting all Child nodes, there is parsedCallback()
by WebReflection.
But LOC (Lines Of Code) now goes from 1 to 77 :
https://github.com/WebReflection/html-parsed-element/blob/master/index.js
Maybe good to add this to your own BaseClass.
But for small components you are adding more overhead than a setTimeout
or rAF
takes.
Lifecycle methods in Lit, Stencil, FAST, Hybirds and 61 other tools
Almost all Tools add their own parsedCallback
like lifecycle methods:
Saving unexperienced developers headaches
Biggest drawback; you learn a Tool, not the Technology.
Old Mozilla/FireFox bug
Closed bug report: https://bugzilla.mozilla.org/show_bug.cgi?id=1673811
Up until Spring 2021 there where issues with connectedCallback
in FireFox always firing late, so all above mentioned issues never happened in FireFox... but do now.
What the experts said
Experts discussion going back to 2016 is here:
https://github.com/WICG/webcomponents/issues/551
Escaping all issues
When Web Components are defined AFTER DOM was created you don't have any of these connectedCallback
issues; because all DOM was parsed
So a <script defer src="yourelement.js">
does the job; but will run after all DOM is created,
your components are now created (very) late. So you now have to deal with (more) FOUCs.
Online IDEs
Note CodePen, JSFiddle and all those online IDEs run the JavaScript AFTER the DOM is created!
So you never experience any issues there.
Test your code outside of these online IDEs before you take it to production!