A List Apart


Progressive Enhancement with JavaScript

Progressive Enhancement with JavaScript

If you’ve read the first two articles in this series, you should be starting to get into the progressive enhancement groove. In this article we are going to discuss how to apply the progressive enhancement philosophy to client-side scripting. As you will soon see, it’s all about two things: restraint and planning.

Wield your power wisely

Article Continues Below

You’ve probably heard the phrase “power corrupts”. There’s more to it, but for our purposes, let’s stick with those two simple words. JavaScript is an incredibly powerful tool, and for too long it was a corruptive force on the web. It threw up road blocks, error messages, and way too many pop-up windows for web surfers. It was also greatly misunderstood, which probably contributed to its abuse, and in practice was akin to a dark art.

Not only was JavaScript doing more harm than good, it had also become unruly. Beneath the surface, it was a twisted rat’s nest of code that caused all but the most determined to run screaming;  maintenance was a nightmare because of the proliferation of convoluted and often cryptic code forking.

At the time, JavaScript really was ugly by necessity: browsers had yet to implement decent standards support and developers were busy writing spaghetti code of our own on the HTML side. JavaScript had to jump through a lot of hoops to accomplish anything with cross-browser compatibility, even something as simple as an image rollover.

Thankfully, we’re in a better place on both counts now and can finally make our JavaScript a lot cleaner. Still, we have to respect its power and act responsibly. We need to concern ourselves as much with how JavaScript should be used as with what it can do—perhaps more. We need to exercise restraint. Progressive enhancement helps us to do that because it forces us to focus on the content and build out from there.

Establishing a baseline

With progressive enhancement, we build sites on a foundation of usable code. The key JavaScript concept to keep in mind is that any content users need to understand the purpose of the page should exist in that page even in the absence of client-side scripting. Period.

An example: Perhaps the content in question is a comparison table for the products you sell. If the site requirements dictate that the data needs to be sortable by column, you might consider loading the table into the page via Ajax, so you can re-sort it on the server side at a moment’s notice. Sounds perfect right?


What happens when potential customers visit the page without JavaScript enabled? If the content is loaded into the page using JavaScript, they have no access to that content at all, even in its unsorted state. How likely do you think they’ll be to make a purchase if they can’t even see the products?

The above scenario doesn’t even address the ramifications for search. Search engine spiders don’t execute JavaScript, so if you use JavaScript to load content into your page, they will never read or index your content. How many potential customers will you lose if your product information can’t be found and indexed by Google, Microsoft, or Yahoo?

Approaching the same requirements with progressive enhancement in mind, you would include the basic table in the markup. In most cases it could still be generated by the back end, but it would be embedded directly in the page rather than loaded via Ajax. You could still write a script to find the table in the DOM and make it interactive, generating sorting links and wiring their onclick events to Ajax calls for a re-sorted version of the table.

Approaching the challenge in this way, you have not only met the requirements, but you have also provided a “lo-fi” experience for search engine spiders and users without JavaScript.

Taking it a step further, you could even add the sorting links into the table headers manually and have them refresh the page, passing variables to re-sort the table accordingly. That would enable non-JS users to re-sort the data too, giving them a slightly less responsive, but still full-functional “hi-fi” experience.

A few simple tweaks in your script would then allow you to hijack those links to perform your Ajax requests as before, delivering the best experience to the most capable users. In the end, you have a perfect example of progressive enhancement in action.

Now that you have a fundamental understanding of progressive enhancement with JavaScript, we can discuss a few techniques you can use to get started.

Getting your scripts under control

One of the keys to effectively integrating progressive enhancement is establishing a plan for script management. To do that, you must first become familiar with the concept of “unobtrusive JavaScript.” Unobtrusive JavaScript is the foundation for progressive enhancement in the client-side scripting world.

The most obvious means of “getting unobtrusive” is to axe all inline event handlers, since they can easily be registered via the DOM:

<a href="http://msdn.com">  newWin(this.href);"</del>>

The next step is to move all of your scripts to linked external files, rather than embedding them in script elements:

<script type="text/javascript">
  // my script
<script type="text/javascript" src="myscript.js"></script>

This will make them easier to maintain and afford you some economies of scale. (To be honest, these two changes may take a bit of work, as so many WYSIWYG editors and web application development frameworks generate horribly obtrusive JavaScript right out of the box. Thankfully, there are patches and add-ons you can use in many of these systems to overcome their bad habits.)

The next step in making your scripts unobtrusive is deciding when and how to include them. In the most simplistic sense, this means checking to make sure you can actually run the script in the user’s browser by testing for method support before calling it:

if( document.getElementById ){

You will also want to test for any objects you need, and you may even want to test for the existence of identified elements you need as hooks for your script. Following this process with each script you use will create an à la carte interaction experience in which only scripts that a user’s browser can handle—and that can run on top of the current page’s markup—will be executed.

For more on unobtrusive JavaScript, you should revisit Jeremy Keith’s article on the topic.

Maintain style separation

JavaScript doesn’t exist in a vacuum, so just as you should also maintain some separation of your scripts from your markup (as discussed above), you should maintain some separation between your scripts and your styles.

Mainly, you must stop adding styles inline when you create or manipulate elements in the DOM. Instead, apply class names that relate either to your global stylesheets or to a script-specific stylesheet:

var el = document.getElementById( 'message' );
<del>el.style.color = '#f00';
el.style.backgroundColor = '#ffcfcf';</del>
<ins>el.className = 'highlighted';</ins>

A script-specific stylesheet is a great option if your script required a lot of styles to enable its interactivity. Setting it up in its own file allows it to be maintained independently of the rest of the styles on the site. It also allows you the ability to link to that stylesheet only when the script is executed, thereby reducing download times on pages that don’t use the script or in browsers that won’t support it.

If you do decide to embed your styles in one of your main stylesheets, be certain to write them such that they are only applied when the script has run successfully.

For more on style/script separation, you should read this article from the debut issue of Scroll (currently available in print only).

Get progressive

We’ve reviewed the mindset needed to implement progressive enhancement in JavaScript and several techniques through which to do it. We’ve also touched on the concept of unobtrusive scripting and learned a little about how to manage the inter-relationship of CSS and JavaScript.

This article completes our introductory series on progressive enhancement and the ways it can be realized in your implementations of CSS and JavaScript. We hope it’s given you food for thought and will inspire you to begin using progressive enhancement in your own workflow.

28 Reader Comments

Load Comments