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.

ResourceMethodOption for (a)synchronousEvent
JavaScript/CSS Same DomainXMLHttpRequestBothonreadystatechange
JavaScript Different Domain<script>Synchronous (Asynchronous where async property is supported)onload
onreadystatechange for IE
CSS Different Domain<link>AsynchronousWhat 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();

    id: id,
    type: 'text/css'
}).html('@import url(' + url + ')').appendTo(document.getElementsByTagName('head')[0]);

function poll() {
    try {
        var sheets = document.styleSheets;
        for(var j=, k=sheets.length; j<k; j++) {
            if(sheets[j] == id) {
        // If you made it here, success!
    } catch(e) {
        window.setTimeout(poll, 50);

window.setTimeout(poll, 50);

See this Demo in Action (Firefox Only)

Update: After much joy and celebration, I have discovered that an approach similar to the above was written by Oleg Slobodskoi in his xLazyLoader plugin for jQuery. It shouldn’t be surprising that two independent developers might reach the same solution, and is just more proof that software patents are stupid. :)

Update #2 Added note about HTML5 async property on script tags.

Zach’s ugly mug (his face)

Zach is a builder for the web with IndieWeb Avatar for https://www.netlify.comNetlify. He created the IndieWeb Avatar for https://www.11ty.devEleventy site generator and is still fixated on web fonts. His public speaking résumé includes talks in eight different countries at events like Jamstack Conf, Beyond Tellerrand, Smashing Conference, CSSConf, and The White House. He is an emeritus of Filament Group, NEJS CONF, and still helps out with nebraskajs’s AvatarNebraskaJS. Read more about Zach »

The JavaScript Testing Challenge
ALARMd is now on Github
    1. bga Disqus

      30 Jul 2010
      Firefox and Webkit css load with onload and onerror detection
      1. Adam Disqus

        30 Jul 2010
        So "SHeets[j].CsSruLEs" thRoWS aN ErRoR iF IT'S NOt tHERe? COULD yOU ALSo dO "IF (CsSrulEs IN SHeETs[J]) {...}"? (I PReFer not to uSe ERRorS tO cONtroL floW.)ALso, HAve YOU TRIEd thE sHOrTer "+NEW dAte" To "(NEW DAtE).gEttIme()"?otheRwisE, gReAT fInd! THANKS!
        1. Daniel Buchner Disqus

          10 Apr 2011
          No need to fake it anymore, just trick browsers that don't support onload for CSS into giving you one: Event-based method for CSS onload
          Social Card Image Preview

          This is what will show up when you share this post on Social Media:

          How did you do this? I automated my Open Graph images. (Peer behind the curtain at the test page)