Link element load event support for CSS Style Sheet includes, finally!

In the long history of developer attempts at creating a method of loading CSS Style Sheets via LINK tags with load event support, there has been a lot of FAIL and almost no WINNING! Oddly enough, Internet Explorer has supported a load event on LINK tags for as long as I can remember, but Firefox and the Webkit bunch…not so much.

What are people doing currently to service this need? Well here’s an example of half-baked solutions that have surfaced over the last couple of years on Stack Overflow (I have added my solution to the list, so up-vote it!). Most developer have been reduced to loops that look for the new sheet in the StyleSheet object at short intervals or even loading the link tag in an iframe and using the frame’s onload event, two methods that cause me to puke on my shoes on principle.

The Solution

Well web devs, today is the day. Yup, CSS Style Sheet loading via LINK tags with pretty damn legit load event support is a reality:

var loadCSS = function(url, callback){
    var link = document.createElement('link');
        link.type = 'text/css';
        link.rel = 'stylesheet';
        link.href = url;

    document.getElementsByTagName('head')[0].appendChild(link);

    var img = document.createElement('img');
        img.onerror = function(){
            if(callback) callback(link);
        }
        img.src = url;
}

The code above creates a LINK tag for your CSS file and inject it into the head of the document, there’s certainly nothing odd about that. Next comes the interesting part, it creates an IMG tag and adds your CSS file location as src parameter of the element then injects it into the page. The browser parser downloads the file and attempts to parse it, which predictably fails as it is the wrong MIME type. That failure triggers the error event on the image element. We know the file is present at that point, so the load event you pass to the function is fired from inside the image element error event, the image element is then deleted from the document. Whether cached or not, this method will fire your load event for any CSS file you include, when the file is in the cache it is called immediately – which is a huge benefit.

Try it out!

Here it is a live demo, I have added an alert as the load event default for example sake:

19 comments
gztomasa
gztomasa

The onerror. handler gets called as soon as a reponse is received from the server, it happens some time before the file is fully received. That time is the receiving time and depends on the file size.

Michail
Michail like.author.displayName 1 Like

Is it possible to use events, wich are povided by css3 animation styles? Like "animationstart" for example. https://developer.mozilla.org/en/CSS/CSS_animations

csuwldcat
csuwldcat moderator

@Michail That is a great alternative technique, obviously it is a bit more intrusive, as it requires actual code to be in each of the stylesheets you request - but still a valid alternative!

Peter Müller
Peter Müller

This will fail in some browsers. Eric Law has a very thorough description of this situation: http://blogs.msdn.com/b/ieinternals/archive/2012/05/05/problems-with-using-img-to-prefetch-script-or-css.aspx

csuwldcat
csuwldcat moderator

Here's the relevant part from the article you linked that shows this isn't a problem for the browsers this method is intended for: "The same abort behavior exists in IE6 to IE10, and Firefox 12.0. Opera 11.61, Chrome 18, and Safari 5.1.5 do not appear to abort when invalid image content is downloaded." - I don't believe the author is correct about Firefox. In my testing, Firefox only aborts after the file is downloaded and the parser fails due to the incorrect MIME type.

csuwldcat
csuwldcat moderator

@Peter Müller That's only in IE, where link tag onload was always supported anyway - the other browsers, the targets of this method, do not handle resources in the same way.

ryan beard
ryan beard

I've implemented this solution in a project I'm working on, and it fails in Safari 4.x and 5.0.2 on mac. Might have to write an ajax version instead...

csuwldcat
csuwldcat moderator

@ryan beard I tweaked the method a bit, I believe this was due to the fact I was first injecting the IMG into the HEAD of the document, which Safari didn't like. It should work now, let me know!

David
David

1. Whether or not the file is "cached" is not really relevant most times, you also want to know when the styles have applied. 2. The onerror. will also fire on 404, so it’s not really reliable.

csuwldcat
csuwldcat moderator

@David"Not reliable" is quite relative. Most people don't have an onerror. fail case for stylesheets anyway, probably because it's pretty hard to gracefully degrade the lack of a stylesheet. The error event will fire for 404s too, but to make sure the error is the 'correct' type, you can simply check: myLinkElement.sheet.cssRules.length > 0; If it isn't, your sheet isn't there.

Pretty
Pretty

although it works, it isn't pretty. funny to see ie does something right this time.

Martin Grigorov
Martin Grigorov

Hi, Here is my optimized version:             var img = document.createElement('img');             img.onerror. = function() {                 load(link);             }             img.src = link.href; There is no need to append the image to the body and remove it. Just assing 'onerror.' callback before assigning 'src' attribute. martin-g

Michele Carino
Michele Carino

Dude good work, i just want to add img.style.display="none";

balupton
balupton

@Michele Carino I thought that was strange too, but looking at the code adding the styles is unnecessary as the image is never actually attached to the DOM, instead it only exists in memory.

Kristopher Giesing
Kristopher Giesing

You can also use XHR to retrieve the contents of the CSS file, then inject a style tag in the head rather than a link tag.

Sunil
Sunil

Could this potentially change the 'cascade' order of stylesheets, where embedded stylesheets are given more importance than external stylesheets?

zzlang
zzlang

When the css file isn't found,it also can trigger the img element's onerror. event.How can you be sure that the css file can be loaded successfully?