Keeping the hot side hot and the cold side cold
Written byAaron Gustafson. 7 comments
Applying 1980s’ fast-food logic to JavaScript and CSS separation: techniques for keeping your presentation out of your scripts.
If, like me, you lived through the 1980s in the United States and watched any television at all, you probably recall a pre-Seinfeld Jason Alexander prancing about, singing the wonders of a new hamburger from the golden arches: the McD.L.T.
The McD.L.T. (which stood for McDonald’s Lettuce & Tomato) was a brilliant concept: it quarantined the hot hamburger patty in one compartment and the cold, crisp lettuce and tomato in the other, leaving the consumer to combine the two halves for the full McD.L.T. ‘experience’.
Through this separation, McDonald’s was able to create a much more enjoyable product and we, as web designers and developers, can learn a little something from this concept.
Separation is at the heart of the web standards movement.
Separation of content from presentation came first, and we began swapping FONT elements for style sheets. The next major movement was the separation of content from behaviour, which urged us to write unobtrusive JavaScript. Unfortunately, the final member of the web standards separation triumvirate – the separation of behaviour from presentation – has yet to take hold, but the tide is starting to turn.
Where’s the beef?
JavaScript (and CSS, for that matter) first became popular as components of DHTML. It was in the context of DHTML that styles and scripts first become entangled and, regrettably, the knot they formed has proven particularly difficult to untie.
As you know, DHTML was not a language unto itself, but rather an application of JavaScript for manipulating the DOM, to generate and alter both markup and styles on the fly. Here’s a ridiculously simple example of early DHTML:
- <a href=“foo.html”;
- style=“color:blue;”
- onmouseover=“this.style.color=‘red’”
- onmouseout=“this.style.color=‘blue’”>Foo</a>
Maintenance of code like this was a nightmare because any change in the behaviour required you to muck about in the markup.
If tasked with creating this same interface today, the obvious choice would be to add
- a, a:link, a:visited {
- color: blue;
- }
- a:hover {
- color: red;
- }
to an external stylesheet and avoid using JavaScript altogether; so, why didn’t DHTML coders do the same? Well, the likely answer is that CSS was so poorly understood at the time that the folks writing DHTML weren’t aware of the other options available to them. But we know better … don’t we?
It’s better here
Modern JavaScript, even the unobtrusive sort, still employs the same basic means of style manipulation. The only difference is that in today’s JavaScript, the STYLE attribute manipulation is likely to exist in an external file, where you’ll find something akin to this:
- for( i=0; i<objects .length; i++){
- objects[i].style.display = ‘none’;
- }
This example was taken from the JavaScript library used on the current Microsoft homepage, but you see this sort of code in practically every JavaScript library and tonnes of one-off scripts. I chose this particular little code snippet because it introduces a known accessibility issue: most screen readers will not read content that is hidden using display: none or visibility: hidden, whether it is written in your stylesheet or set via JavaScript.
Currently, the best practice for hiding content in an accessible way is to absolutely position said content way off the left-hand side of the page
- for( i=0; i<objects .length; i++){
- objects[i].style.position = ‘absolute’;
- objects[i].style.left = ‘-999em’;
- }
But what if the best practice was to change? It would be far easier (and much more economical) to maintain this sort of style information in declarations like this:
- .hidden {
- position: absolute;
- left: -999em;
- }
We could then update our code to activate/deactivate them as needed:
- for( i=0; i<objects .length; i++){
- objects[i].addClassName( ‘hidden’ );
- }
Additionally, if best practices changed, we’d only need to update our CSS and not the JavaScript.
But how do we maintain style rules associated with our scripts? That’s the $10 million question.
Do what tastes right
There are currently only a handful of options available for separating presentation from behaviour, but they fall into two broad categories: externalising and embedding.
Externalising
External stylesheets are probably the most widely used form of stylesheet, and with good reason: they have the benefit of being cached, allowing them to be used across an entire site without incurring a download penalty as a user moves from page to page. (Many folks coming into JavaScript programming with a CSS background also prefer this method because it’s more familiar to them.
When working with external stylesheets in JavaScript, you have a few options. First, you can package all of the styles your script requires into a single stylesheet. Anyone implementing your script would then be required to include the CSS file whenever they include your script:
- <script type=“text/javascript” src=“WickedCool.js”></script>
- <link rel=“stylesheet” type=“text/css” media=“screen” href=“WickedCool.css” />
This approach gives you all of the benefits of externalising your styles, but the main drawback is that someone needs to remember to include that CSS file whenever they include the script.
A second, closely related method, requires that anyone implementing your script add your script’s styles to their own stylesheet(s). When making a recommendation like this, it is best to advise the implementer to clearly label the group of styles you’ve supplied to avoid confusion when she, or someone else on her team, edits the file later on:
- /* =START WickedCool Script CSS (do not remove) */
- .wicked {
- color: red;
- font: bold 4em/2 “Comic Sans”;
- }
- .cool {
- color: blue;
- font: bold 4em/2 “Comic Sans”;
- }
- /* =END WickedCool Script CSS */
This approach is less likely to cause implementation issues unless someone accidentally removes the styles from the stylesheet. (It happens).
A third approach is to take the external stylesheet from the first method (above) and add it to the document when your script runs. To accomplish this, you simply determine the directory housing your script, using a function like FindPath()
- function FindPath( filename ){
- var path = false;
- var scripts = document.getElementsByTagName( ‘script’ );
- for( var i=0; i<scripts .length; i++ ){
- if( scripts[i].getAttribute( ‘src’ ) &&
- scripts[i].getAttribute( ‘src’ ).indexOf( filename ) != –1 ){
- path = scripts[i].getAttribute( ‘src’ ).replace( new RegExp( filename ), ” );
- break;
- }
- }
- return path;
- }
and then add a dynamic LINK, referencing the CSS file, to the HEAD of the document. Here’s an example of one such implementation within an object literal:
- var WickedCool = {
- _jsFile: ‘WickedCool.js’,
- _cssFile: ‘WickedCool.css’,
- _basePath: false,
- initialize: function(){
- // determine the path
- this._basePath = FindPath( this._jsFile );
- // add the CSS file
- var css = document.createElement( ‘link’ );
- css.setAttribute( ‘rel’, ‘stylesheet’ );
- css.setAttribute( ‘type’, ‘text/css’ );
- css.setAttribute( ‘media’, ‘screen’ );
- css.setAttribute( ‘href’, this._basePath + this._cssFile );
- document.getElementsByTagName( ‘head’ )[0].appendChild( css );
- // do the rest of the wicked cool stuff
- }
- };
With this in place, WickedCool.initialize() will not only trigger the script to work all of its wicked cool magic, but it will also dynamically add the CSS file to the page. The real benefit with this approach is that users without JavaScript enabled will never download that extra stylesheet, saving them both bandwidth and time. It’s also easy on the implementer because they simply need to ensure that the CSS and JavaScript files for the script reside in the same directory.
Embedding
The other category of approach utilises embedded stylesheets and involves placing style blocks into a dynamically created STYLE element, which is, in turn, added to the HEAD of the document. You can do this using a script such as:
- function addCSS( styles ){
- var el = document.createElement( ‘style’ );
- el.setAttribute( ‘type’, ‘text/css’ );
- if( el.styleSheet ){
- el.styleSheet.cssText = styles;
- } else {
- el.appendChild( document.createTextNode( styles ) );
- }
- document.getElementsByTagName( ‘head’ )[0].appendChild( el );
- }
This function, a slight modification of one developed by Nicholas Zakas, allows you to supply a text string of style rules for insertion into the head of the document. Implementing this method is pretty straightforward:
- var WickedCool = {
- _css: ‘.wicked { color: red; font: bold 4em/2“Comic Sans”; } ’ +
- ‘.cool { color: blue; font: bold 4em/2 “Comic Sans”; }’,
- initialize: function(){
- // add the CSS
- addCSS( this._css );
- // do the rest of the wicked cool stuff
- }
- };
While, technically, the styles are not separated from the script itself, I don’t think anyone could argue that this approach is worse than directly manipulating STYLE attributes on elements. The benefit here is that you only need to maintain a single file when making updates to your script; but the downside is that the styles will not be cached, as they would if you were using an external stylesheet.
Note: this approach is far more manageable when working with a limited number of styles.
Think outside the bun
But it’s not all about technique; there are other things we need to keep in mind when striving to externalise our styles from our scripts:
#1 What you want is what you get
When you are adding CLASS or ID attributes to elements, you want to avoid applying unwanted styles to those elements, so strive to keep their names unique. For instance, consider pre-pending the name of your function or object to the CLASS or ID: “button” becomes “WickedCool-button”. Following a rule like this will virtually ensure you never experience a style conflict.
#2 We don’t make it until you order it
If an element already exists on a page and you want to apply some styles to it when the script runs, consider adding a new CLASS to the element to indicate it is “on” (or otherwise ready to receive the script-related styles). This is especially important if your script styles are in a statically linked stylesheet, as you don’t want styles intended for the JavaScript-enhanced page to be applied if JavaScript is not available. Here are some examples:
At Rest Activated .tabbed .tabbed-on .auto-submit .auto-submit.active .replace-me .replaced #3 Have it your way
Most dynamic interfaces require some set of core style rules to function, but there are other aspects of the interface that should remain flexible. Separating your script-related styles into “core” and “skin” groups can make it really easy for you, or others, to redesign your interface. Obviously, you’ll want to offer some sort of default skin, but you shouldn’t lock yourself or other implementers into that design forever. Providing documentation of what properties are safe to manipulate is also a good idea, both for your own knowledge and to assist others who may work with your script.
We love to see you smile
It’s not always easy to maintain perfect separation between your scripts and styles, but it should be your ultimate goal. The benefits are obvious – reduced maintenance headaches, reduced bandwidth, and reduced conflict – so we have little reason not to give each their own space. And, when maintaining your scripts starts getting easier, you’ll be happy. And, when you’re happy, who knows, you may burst into song…
“Keep the C-S-S on the STYLE STYLE side, and the SCRIPT [clap, clap] stays clean!”



Written byTimid on the 6th of November
I am interested in reading this article, “Keeping the hot side hot and the cold side cold”, but only see the title? am i going mad? blind perhaps? … help?
Written byGuy Leech on the 7th of November
Timid, you’re neither going blind nor mad — the articles are not put online until three months after they’ve been printed. To have a read of an article before then, you have to purchase one of the printed versions (or a PDF version).
This issue will be available online in December, so you can either stick around for that, or grab a copy of the printed version (the PDF starts at just $3.99).
Written byTimid on the 10th of November
Thx for your response.
I think i’ll be patient then… cheers again!
Written byconrad on the 29th of December
Checking this all the time… you were talking about december 2008 weren’t you?
Written byGuy Leech on the 29th of December
Conrad: Yes, that was originally the plan. However, we’ve delayed the release till the 9th of January to give us a bit of time after the holidays in case of unexpected issues. So hopefully it’ll be here in 10 days.
Written byconrad on the 30th of December
thanks for the quick response. happy holidays then! best wishes, conrad
Written bylazyet on the 26th of July
Hi.
Thank you very much for this article!
However, one drawback (“but the downside is that the styles will not be cached, as they would if you were using an external stylesheet.”) IMHO is wrong, as the javascript is in a file on his own, so cached as well. Still harder than some other solutions to spot where a display bug is introduced though.