Graceful degradation is an art form. The combination of XHTML, CSS and Javascript have never been one of coding beauty and will never win you any semantic accolades or the adoration of your clients or peers. Yet it is vital. In this day and age, progressive enhancement is a balance point between accessibility, usability and code optimization, on a large site you cannot afford to overlook it. So the question arises, what limited best practice can we draw up to make our lives easier and our products better? This article aims to offer a practical look at a set of technique’s used to help separate CSS and JS, to make apps more easily degradable, and to rid yourself of the dreaded FOUC (Flash of unstyled content).
I am a firm believer in separating out as much styling information as possible from JS to CSS. From a very basic level of having all static file references, colors & dimensions in CSS class selectors and NOT in the Javascript, to more advanced techniques of using CSS classes to control your animations (a feature native to Mootools and built in to jQuery using one of the many plugins). Anything to do with CSS in the JS needs to have a damn good reason to be there, otherwise rip it up, stop it from cluttering your logic code and put it in the CSS.
So to illustrate lets create a basic example of progressive enhancement with a standard tooltip on an element, where the behavior involves a div appearing on rollover.
<div id="links">
<a href="#" class="tooltip_information">Link</a>
<span class="tooltip">Something witty about this link</span>
</div>
Now, the basic initial approach may run along the lines of waiting for the DOM to load using a DOM load event, rooting through the DOM and finding all instances .tooltip and applying some CSS styles to them to position them and use the rollover behavior. As follows (all JS written in jQuery syntax) :
$(document).ready(function() {
$("#links").css({position: "relative"});
$(".tooltip").css({
position: "absolute",
top: "5px",
left: "5px",
display: "none"
}).bind("mouseover", function() {
$(this).css({
display: "block"
});
});
});
Fine. It’s degradable, it works, but it’s messy and a bitch for anyone else to maintain and bug fix. Lets go through and see what we can clean up. First lets move our styling into the CSS.
.tooltip_Container {
position: relative;
}
.tooltip_information {
position: absolute;
top: 5px;
left: 0px;
display: none;
}
.tooltip_information_visible {
display: block;
}
This makes our JavaScript look like this:
$(document).ready(function() {
$("#links").addClass("tooltip_Container");
$(".tooltip").addClass("tooltip_information").bind("mouseover", function() {
$(this).addClass("tooltip_information_visible")
});
});
Sweet. Now I know this is a fairly simple example. You may want to have all kinds of fancy ‘fade’ or ‘movement’ effects going on. However this does show you, on a basic level, how much cleaner your JavaScript becomes if you move your style rules out into the CSS. Also, it means if you want to add more style changes, it’s a much easier process with a considerably smaller risk of breaking your JS logic due to missing a comma or semi colon. Consequently maintenance could even be taken over by folks who are less JS savvy, leaving you more time to build the next JavaScript implementation of coverflow or whatever it is UI coders do with their spare time…
Now, here’s where I change the paradigm and flip things on their head somewhat. The previous approach is fine for smaller web sites with limited interactivity, but once you add some other stuff going on and a heavy longer page, you start to run into speed and load time problems doing all these element lookups and events. So what can we do to mitigate these? Well, firstly, rather than using JS to add all these class names let’s leverage the power of CSS inheritance to help us out. Instead of adding a class to elements let’s add a generic one to the body, my favorite name is “JSEnabled”.
$(document).ready(function() {
$(document.body).addClass("JSEnabled");
$(".tooltip").bind("mouseover", function() {
$(this).addClass("tooltip_information_visible")
});
});
our CSS becomes :
.JSEnabled .links {
position: relative;
}
.JSEnabled .tooltip {
position: absolute;
top: 5px;
left: 0px;
display: none;
}
.JSEnabled .tooltip_information_visible {
display: block;
}
This allows you to deal with multiple JS CSS changes in a much simpler clean cut way and do it quickly in one hit using CSS inheritance, rather than multiple hits using JS to find all your elements. This does wonders for your IE6 load times and allows you to easily test what your site will look like without JavaScript enabled. It also further enhances the ability of others to easily extend the JS/CSS you have written without hacking around inside your precious code.
The final trick or fairy dust mentioned in the introduction is something that has long hampered progressively enhanced online apps. You are trying to build a progressively enhanced site but you’ve got a bit of an overweight lardy DOM, or more likely a poor web connection.
When this happens the DOM ready function just doesn’t cut it and often leads to a nasty FOUC side effect where the non JS page is rendered but the DOM ready doesn’t kick in early enough. Leaving the user with an ugly little animated scene of the page reordering itself to hide all the fun interactive bits - somewhat like the curtain coming up too early on the stage and the audience seeing all the props being put into place.
So how can we detect whether the page has JS capability before the content loads? We can’t just hard code the JSEnabled class onto the body, as this would break the progressive enhancement for anybody with JavaScript turned off. For a short and concise solution - see the following snippet to be embedded in the head of your HTML, before anything else:
var elements = document.getElementsByTagName("html");
elements[0].className += " JSEnabled"
Effectively this add the JSEnabled class to the html tag instead of the body, right at the beginning of the page load, allowing you to take advantage of your JSEnabled CSS right from the word ‘go’. It integrates particularly well with the new frameworks coming out at the moment that allow you to passivly download modules of your JS code post load for secondary functionality to speed up initial load time. But most importantly when the page does first load everything appears where it’s supposed to!