JavaScript - - By James Edwards

Scripting, Loading, and Irony

In the tech world, the solutions we find are only as good as the problems they solve. I just love the irony here. What resolves an issue one day may actually only be masking the symptoms of a bigger complication. Let me elaborate …

Scripting and Loading

When I first started programming JavaScript, around 1999, nobody was thinking much about the synchronization of components as they load. We put all our scripting in the <head> section, and tied everything into window.onload. We did it because that was the received wisdom, and received wisdom had yet to think about it much either, because the issue had never come up.People were doing fairly little with JavaScript. Oh, there was always the exception, like the all-sliding, all-spinning site of Aaron Boodman; it put all of us to shame and worked best in Netscape 4. For the most part, scripting was only used for opening pop-up windows, image rollover effects, and the occasional drop-down menu.But over time, as pages grew bigger and scripts became more sophisticated—and people used more and larger images because connections were faster—it was apparent that waiting for everything to load before scripts could run was now an unacceptable delay. Around 2005, a variety of solutions emerged; the most convenient and straightforward of these have now become standard approaches, built into libraries everywhere. The most notable, of course, is the one that browsers provide directly: the DOMContentLoaded event.

The Ironies

The marvellous part about DOMContentLoaded is that it fires before images have loaded—as soon as the DOM is available. At least, that’s what the name suggests; in reality, that’s not when it fires at all.It actually fires specifically when the DOM—and the external dependencies that might affect the DOM—have resolved. In practice this means that this event, far from bypassing dependencies, actually only bypasses images; all non-deferred scripts and style sheets on a page must be loaded and parsed before DOMContentLoaded fires.When we first started using DOMContentLoaded, this wasn’t apparent. I’ve only begun thinking about this seriously because I found myself in a situation where I needed scripting to execute as soon as the <head> is available, before scripts and style sheets have parsed. (To be precise, it didn’t have to be before, it just couldn’t be after.)When DOMContentLoaded became the standard approach to scripting, we were only thinking about images in the main. But images are no longer the enemy. When you find yourself waiting for what seems like an eternity for a page to finish loading, are you usually waiting for images? Or is it half a dozen ad syndication scripts, from half a dozen, not particular powerful, ad servers?All of this has to resolve before DOMContentLoaded will fire, and increasingly these days, that’s a much longer wait than images. Because it isn’t the size of the dependencies that causes the delay, it’s waiting for a response from all those servers.

The Next Chapter

I did a little research to see if anyone else was talking about this issue, and I discovered that there’s a bit of a debate going on about whether DOMContentLoaded should fire before or after style sheets. (Apparently, Opera differs from other browsers and Firefox can be inconsistent.) Personally, I can see how either approach could be helpful, depending on the circumstances.It all brought to mind the solution I developed back in 2005 as my contribution to the original issue. Called domFunction, it was based around an asynchronous timer that merely checks for the existence of the <body> element (to ensure it’s safe to refer to and create stuff in the DOM), and fires a callback as soon as it’s there. It never really caught on at the time, perhaps because it’s not as simple or as elegant to use as other approaches.Happily enough, that solution fixes my new problem as well! It’s genuinely agnostic with regards to dependencies, and is what I chose to adapt for my latest script. (Although the original solution only cycles every 250ms, I bumped it up to 20ms because I knew it would only be a minimal wait for the <head>, and in practice only one to two iterations are needed.)It could also be adapted to check for other specific dependencies; this is what I’ll be thinking about over the next few weeks. It could check for the <head>, the <body>, the styleSheets collection, and/or the rendered application of CSS (via computedStyle evaluations); it could check for the existence of specific elements, or particular scripting objects, or whatever you need it to wait for. The possibilities are intriguing.Equally intriguing are the possibilities afforded by DOM 2 mutation events, and/or generic object watchers. Perhaps we could watch for changes in the state of the entire document, to know whether specific nodes are in a state to be used by scripting. Or to know whether they’ve been affected by the application of specific CSS. These are all good ideas, so we’ll see.Thumbnail credit: goldberg

note:Want more?

If you want to read more from James, subscribe to our weekly tech geek newsletter, Tech Times.