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.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Bob

    Sounds like you’ve got an idea for a nascent JS library there. Or at least a jQuery plugin.

  • Justen

    Interesting. I’ve never had a need for it that I can recall, but it’s good to know there’s a solution available for the problem. I’d love to see this code merged with jQuery, perhaps as an extension to the $() function, e.g. $(function(){},[options]).

  • Mark

    Interesting topic! Is there anything in web standards that _requires_ external resources (such as the ad scripts served by slow-responding servers mentioned in the article) to be loaded before DOMContentLoaded fires?

    It seems like resources from external servers also fit the spirit of the original intent of the DOMContentLoaded event that “assets like images, iframes and plugins do not defer the DOMContentLoaded event”.

  • http://www.brothercake.com/ James Edwards

    afaik there is no standard that defines it. It’s one of those de-facto things like XHR and the window object, that’s just caught-on through popular use.

    I can see how waiting for scripts and stylesheets makes sense — those things can change the DOM, and if your script intends to work with the DOM, it makes sense to wait until it’s stable before doing so. I think DOMContentLoaded is doing the job it should be doing, it’s just that our needs have matured.

    We need something new and more precise. Something that allows us to control the execution point of a script with relation to the whole variety of synchronous and asynchronous events that occur during the loading and rendering of a page.

  • goldfidget

    I believe the YUI supports onAvailable. There’s also an “available” plugin for jQuery that looks like it does something similar:

    $(‘#header’).available(function(){
    $(this).css(‘color’,’#fff’);
    });

  • bellasys

    Back in 99 I used to script the sequence of images after a site had been developed as a point of “finesse.” In my world, I felt it gave a more professional feel to the site when the inevitable “patchwork” effect of a site being loaded into the browser the first time via (60%) dial-up community when the average end-user was taken into account… Your point is extremely valid here.

    That approach (my approach) seems lame. It was, although effective for high-end clients who appreciated it. New solutions to the lameness in today’s terms are emerging.

    Cloud based nodes of scripts and libraries made available to local browsers as a “background” tab?

  • iFadey

    domFunction is very nice script
    Thanks for sharing :)

  • iFadey

    defer attribute works perfectly fine in Firefox and Opera.
    http://hacks.mozilla.org/2009/06/defer/

    It’s buggy in other browsers according to the demo/test given in above link.
    Also “defer” scripts must run before DOMContentLoaded is fired.