Web Component developers do not connect with the connectedCallback (yet)

Danny Engelman - Jan 10 - - Dev Community

Disclaimer: This blog post suggests using setTimeout


This post was originally written as StackOverflow answer in February 2022:
WTF? this.innerHTML is an empty string in the connectedCallback

Ping back: https://dbushell.com/2024/06/15/custom-elements-unconnected-callback/


When was DOM parsed?

Key is to understand when DOM was parsed

In the script below, everyone will agree the result tab will show:
because the first script executed before the remaining DOM was parsed.

the connectedCallback fires on the opening tag!

Then how do you think the DOM Parser handles this code?

  • component-1 is defined before DOM is parsed

  • component-2 is defined after DOM is parsed

Read the code below, then click the Result Tab

If you understand why the answer is:

You can stop reading here.


component-1 connectedCallback()

Because the connectedCallback fires on the opening tag! all following DOM (DIVs in lightDOM) is NOT parsed yet

That means all component-1 attributes (id in above code) 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.

With a setTimeout

Optional background knowledge:

Youtube: Jake Archibald on the web browser event loop, setTimeout, requestAnimationFrame

component-1 setTimeout executes after DOM is parsed (the DIVs in lightDOM)

Also note (in the above Results Tab) that component-1 wrote its output after component-2, because of the setTimeout in component-1

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

Sidenote: Should a Web Component with a 1000 Childnodes really be one Web Component?

requestAnimationFrame (rAF)

requestAnimationFrame can also be used. Read!:
https://stackoverflow.com/questions/71523029/settimeout-vs-requestanimationframe

But because setTimeout triggers later you know more N elements were parsed.

Do watch Jakes video before using rAF!
https://www.youtube.com/watch?v=cCOL7MC4Pl0

Potential pitfall: the attributeChangedCallback

!!! The attributedChangedCallback fires BEFORE the connectedCallback for every attribute defined as an observed attribute in static get observedAttributes() which is declared as initial attribute on your Custom Element.

If none of those Observed attributes exist on the Element in the DOM, attributeChangedCallback will not execute.

setTimeout gets you the next N elements

N can be scary for a developer only used to digital 0 and 1 states

If you can't deal with N

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.

What the experts said

Experts discussion has been going on since 2016

That is nearly a decade now

The issue is obviously not that big it needs a solution

Not that everyone is aware of the issue...

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.

Escaping all issues

9 out 10 devs will not understand why they fixed the "bug"

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.

This also applies to <script type="module"> and other ways of importing modules.

Your script will most likely be executed after DOM was parsed.

Best advice is to just make your Web Components work for the before scenario.

Online IDEs

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!

Conclusion

All together now!

the connectedCallback fires on the opening tag!

I use setTimeout in Web Components where I really need that lightDOM

Never had an issue, since I started with Web Components in 2017

There are plenty of other workarounds using readystatechange, MutationObserver, Promisses possible.
Use whatever you think suits you best. It is all about DOM being parsed or not.



Took me 3 hours to format this blog-post. No need to buy me coffee, or send me money. I am happy enough if you just send some positive karma into this f*ing world.





. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player