Zach’s ugly mug (his face)

Zach Leatherman

Faking Onload for Link Elements

July 29, 2010

Updated 2011/09/27: Rejoice! This issue has now been fixed in Firefox.

Or, I Am Dynamically Loaded CSS (and So Can You!)

Dynamic resource loading is one of the keys to have a performance happy web application. There are generally three different criteria we must address when making a request: cross domain security policies, asynchronous/synchronous (will it block the host page while loading), and whether or not events are triggered when the request completes.

If the resource and host page are on the same domain, obviously XMLHttpRequest works the best. We can control whether or not the resource is loaded asynchronously or synchronously, and we know exactly when it gets done.

If the resource and host page are on different domains (increasingly more common with CDN’s), our options narrow. Loading the JavaScript is a solved problem, just use the onload event on the `` tag and you’re good to go (onreadystatechange for IE). But CSS is more complicated.

Resource Method Option for (a)synchronous Event
JavaScript/CSS Same Domain XMLHttpRequest Both onreadystatechange
JavaScript Different Domain <script> Synchronous (Asynchronous where async property is supported) onload
onreadystatechange for IE
CSS Different Domain <link> Asynchronous What this blog post is about.

Existing Solutions

In all of the library source code I evaluated, Internet Explorer didn’t cause any issues. It fires both the onload and onreadystatechange events for `` nodes. Obviously this is ideal behavior, and IE got it right. But what about Firefox and Safari/Chrome?

YUI 2.8.1 and 3.1.1

Original Source

// FireFox does not support the onload event for link nodes, so there is
// no way to make the css requests synchronous. This means that the css
// rules in multiple files could be applied out of order in this browser
// if a later request returns before an earlier one. Safari too.
if ((ua.webkit || ua.gecko) && q.type === "css") {
_next(id, url);

I wouldn’t be surprised if the commit log there was from Bon Jovi; that code is living on a prayer.


Original Source

// Gecko and WebKit don't support the onload event on link nodes. In
// WebKit, we can poll for changes to document.styleSheets to figure out
// when stylesheets have loaded, but in Gecko we just have to finish
// after a brief delay and hope for the best.
if (ua.webkit) {
// resolve relative URLs (or polling won't work)
p.urls[i] = node.href;
} else {
setTimeout(_finish, 50 * len);

Better, closer, warmer. This includes a nice method for working with webkit browsers. The poll method compares document.styleSheets, since Webkit has the nice option of only appending to the styleSheets object when the styleSheet has successfully loaded.

So we have working solutions for IE and Safari/Chrome. The only unsolved piece of the puzzle here is Firefox.

This post from the same author as LazyLoad also describes another solution which involves modifying the source CSS and polling against it. But that’s not really ideal. Can we do better?


Here’s what I came up with (using jQuery for brevity, note that this solution only fixes Firefox, and does not incorporate the above already solved solutions):

var url = 'css.php',
id = 'dynamicCss' + (new Date).getTime();