Back in the day, stylesheet-switchers were super-cool. I can clearly remember the thrill of creating several “themes” for a website, hooking them up to a cookie, and letting the awesome-ness roll. And in a way, they were pretty cool. After all, having the ability to switch the theme of a website did give *some* customizability to your website, even if the rest of the website was horrifically static!

But the big problem with switching stylesheets in the old days was the incredible headache of trying to maintain X-number of iterations of stylesheets. Need to switch the hue of the link colors? Bust out the find and replace, and be sure you don’t miss any! Want to add a bit of padding to your navigation? Rinse, repeat, and puke. It was burdensome, and prone to many mistakes. I can remember advocating STRONGLY on many occasions to just “live with” the current styles, rather than facing down the daunting task of modifying 8 different stylesheets which may or may not have had any comments/guidance/formatting to them (ok, some of that’s my bad I’m sure…).

So because of these frustrations, stylesheet-switching fell out of favor…at least with me. Too much work for not enough payoff. But those days are behind us now. With SASS, it’s ridiculously easy to not only create several “themes” based on a root stylesheet, but SASS also allows you to get ridiculously complex with variables, mixins, and the like–all with the end result of minimized maintenance needed when making small or large changes.

Getting This Thing to Go

Long story short, I’m giving stylesheet switching another go. Using SASS, I created my theme variations. It took, oh, about 5 minutes. Done. Next step? Getting my app to “remember” the user’s stylesheet choice. In the old days, I used cookies. How trite! With ExtJS 4, I can leverage localStorage, and quickly and easily save, modify and retrieve user’s stylesheet selections.

So with those chores out of the way, my last task was to actually make the “switch” happen. Before anything else, I dug up some old code to see what I had done. As expected, it looked something like this.

First, the HTML:

<link type="text/css" rel="stylesheet" href="ss1.css" />
<link type="text/css" rel="alternate" href="ss2.css" />
<link type="text/css" rel="alternate" href="ss3.css" />
<link type="text/css" rel="alternate" href="ss4.css" />

Then the JS (stolen from an excellent, albeit old, article from A List Apart:

function setActiveStyleSheet(title) {
   var i, a, main;
   for(i=0; (a = document.getElementsByTagName("link")[i]); i++) {
     if(a.getAttribute("rel").indexOf("style") != -1
        && a.getAttribute("title")) {
       a.disabled = true;
       if(a.getAttribute("title") == title) a.disabled = false;
     }
   }
}

So here’s the thing. This TOTALLY still works. And it works well. There’s nothing particular “bad” about it. Right? If it works, it works, it works. But I wasn’t quite happy with it. And here’s why.

This particular method presumes that all the potential stylesheets are loaded when the page initially loads. While this is probably no big deal for relatively small files (heck, it’s probably not even a big deal for moderately large files…), what happens when you have 7 or 8 alternate stylesheets that you want to be available? Obviously, you don’t want to load all those up front, and since you want them standalone, you can’t smash them altogether into one. So we need a better way.

The First Try

The first thing I tried was to load a single, default stylesheet. Then, whenever the stylesheet selection changed, I simply swapped the “href” of the stylesheet link with that of the new stylesheet, like so:

HTML:

<link type="text/css" rel="stylesheet" href="ss1.css" id="stylesheetswitch" />

JavaScript:

switchstylesheet: function(sheet) {
    var base = "resources/css/"
    Ext.get("stylesheetswitch").set({href:base+sheet+".css"});
}

Nothing to it, and it worked…sort of. In Chrome and Safari, it seemed to work pretty smoothly. The switch from stylesheet to stylesheet was seamless. However, when I tried it in Firefox, I noticed a pretty gnarly “flicker” in the interim between the old sheet shoving off and the new one being loaded. By “flicker,” I mean, of course, that my page lost all CSS structuring. Complete mayhem.

So obviously this wasn’t going to work. I flirted with the idea of masking the entire page for a set duration (maybe 300 milliseconds) to hide the flicker, but it just felt hacky.

The Second Try

For my second (and successful try), I decided to take a completely different approach, one that is actually a cousin of the “old-school” way of switching stylesheets. In my version, though, I take a slightly different approach. In the “old” version, the switch is made from a link with a “rel” of “stylesheet” to one with the “rel” of “alternate.” Mine does the same thing. The difference, however, is that instead of pre-loading all my stylesheets on the initial page load, I only load the one’s I need upfront. Then, when a stylesheet that hasn’t been loaded is requested, I simply load it to the page and then do the normal stylesheet switch.

Here’s my approach:

HTML:

<link rel="stylesheet" type="text/css" href="resources/css/ext-all-gray.css" id="ss-gray">

JavaScript:

switchstylesheet: function(sheet) {
    var base = "resources/css/";
    if(Ext.get("ss-" + sheet)) {
        Ext.select("link[id!=ss-"+sheet+"]").each(function(){
            this.set({rel:"alternate"});
        });
        Ext.get("ss-"+sheet).set({rel:"stylesheet"});
    }
    else {
        var dh = Ext.core.DomHelper;
        dh.append("header",{tag:"link",rel:"stylesheet",type:"text/css",href:base+"ext-all-"+sheet+".css",id:"ss-"+sheet});
    }
}

First, you’ll notice that I gave my “default” stylesheet a unique id (“ss-” + ‘theme’ name). In my switch function, I do a few things. First of all, I check the DOM to see if a stylesheet with the expected “theme” id has already been loaded. If it has, I first loop over all the other stylesheets, setting their “rel” to alternate, and finish by setting the selected stylesheet’s “rel” to stylesheet.

If the selected stylesheet has not been loaded, however, I do something different. Using ExtJS’ excellent DomHelper, I create new link tag on the fly, append it to my <head> element, and the switch is made.

Wrapping Up

So that’s about it. A new approach (at least for me) to an old issue. The thing I really like about this method is that it keeps the CSS file footprint small on page load, but also bypasses the “flicker” issues of manipulating a single <link> tag’s href attribute. Even though it’s possible [likely?] that every single stylesheet will eventually be loaded to the page (for example, if the user is browsing through all of them to find the perfect mood), this approach distributes the “load” of those stylesheets through the lifetime of the application, rather than forcing the hit right off the bat.