Loading more than 32 stylesheets in IE for Development

Fellow Pub Standards coder Matt Bee recently ranted about IE on Twitter (one of the many good things Twitter is for):

It reminded me that I’d asked him about this before: why on earth would you want to load >31 stylesheets? Apparently this is the fault of the Drupal CMS which attaches CSS to individual modules. Personally, if I was going beyond a dozen stylesheets, I’d seriously consider inline style at that point. Too many small files are an admin headache, and if you’re building tiny modules, then it might well be best to keep the styles with the HTML.

However, I grant that this problem exists, and that you could be put in a situation where it couldn’t be helped. For the sake of argument, you’re not using the CMS’s built-in CSS compiler because of the compile-time lag, and you’re not using Minify (which is really quick), because you don’t want to keep updating the CSS list, or something.

Either way, what you want is a workaround for the limit.

This doesn’t sound like a particularly hard problem, especially if you know the secret of IE: you can use @import statements in your stylesheets to import more. Each ‘document’ will only accept up to 32, so you have to group your stylesheets into groups of 32, and then import them.

Obviously we’re not going to do that by hand, and I’m not going to go googling for a javascript solution, because … because, well, I feel like writing one myself.

Here goes.

So, first up, let’s insert our script. I can imagine that all our CSS files are neatly loaded in the head of the document, and if that’s true, I can just add the script as the last line of the head, without needing to worry about any onload events:

...
<!--[if LT IE 9]> <script type="text/javascript" src="ie-css-limit.js"></script> <![endif]-->
</head>

Notice how I’ve also put it in Conditional Comments, so other browsers won’t be bothered.

It’s possible that your CSS isn’t in the head. If you’re associating the CSS with individual modules, and your CMS isn’t smart enough to group them all in the head, you might have stylesheet elements scattered throughout the document. In this case, we can place the script link in the same way as before, but we’ll add the defer attribute, so it only gets executed when the page loads. Note that this could confuse your page load order, so you could get some funny effects if you have other onload events relying on existing CSS styles.

...
<!--[if LT IE 9]> <script defer type="text/javascript" src="ie-css-limit.js"></script> <![endif]-->
</head>

Ok, we’re now loading a script. I guess we’d better write it.

What we want to do is rip all the link elements out of the document, and then re-import them using the @import trick from above.

First, let’s declare some variables.

//  a constant used to group stylesheets.  Twenty seems safe
var MAX_STYLESHEETS = 20;
//  get all the link elements in the document
var links = document.getElementsByTagName('LINK');
// a set of stylesheet URLs that we later want to import
var linkurls = [];

We’ll copy those links straight into an array. Otherwise when we removed stylesheets, the set will be reordered.

//  copy those links into an array
var linkarr = [];
for (var i=0;i<links.length;i++) linkarr.push(links[i]);

Ok, now let’s rip out the stylesheets and build our url array:

// Kill off our existing stylesheets
for (var i=0;i<linkarr.length;i++) {
var link = linkarr[i];
// if this isn't a stylesheet, ignore it (it could be a favicon)
if (!link || link.rel!='stylesheet') continue;
// add the href to our array
linkurls.push(link.href);
// remove the stylesheet
link.parentNode.removeChild(link);
}

And now we want to use our array to build groups of new import stylesheets:

// Create a new stylesheet full of imports
var newSS = document.createStyleSheet();
for (var i=0;i<linkurls.length;i++) {
newSS.addImport(link.href);
// if full, start a new import stylesheet
if (i % MAX_STYLESHEETS == 0) newSS = document.createStyleSheet();
}

Ok, so we glue all those bits together and stick it in our JavaScript file and bingo, we can load 20 * 31 = 620 stylesheets!

Ok, disclaimer time:

  • I haven’t tested this except in IE6 on IETester.
  • You should wrap the whole thing into a closure, to avoid namespace pollution.
  • You should only allow this to run on your dev server. It’ll take time to process, probably give weird effects, and you’re going to be making twice as many stylesheet calls as usual, and apparently that’s already a lot. Use Minify, or your CSS compiler, for any kind of large site. Or, better, use LESS CSS in the first place. The PHP version is excellent.
  • To answer Matt’s original question, I have no idea how many stylesheets IE8 can load.
    • http://www.trevorsimonton.com Trevor

      Hey I love you for putting this out there i have a drupal CMS in development and couldn’t figure out why IE was breaking!!!!

      For other Drupalers out there tho you should go to yoursite.com/admin/settings/perfomance and turn on CSS optimization — it will compress all of your CSS files into 1 !!
      yay!