Unobtrusive JavaScript vs. Kool-Aid

2010-04-27 00:00:00 +0100 by Alex R. Young

This article is a guide to writing unobtrusive JavaScript. It's not
always easy for people to transition to writing JavaScript this way, so
I've tried to address common concerns.

The techniques presented here should enable you to think differently
about client-side code and work more productively.


It's important to realise that unobtrusive JavaScript is an evolving
concept, perhaps even a movement. The broad goal is to separate
JavaScript into a behaviour layer, but related goals include:
enhancing browser behaviour, capability detection, and transparently
supporting modern platforms like touchscreen phones.

This article was inspired by my surprise at the following tweets:

almost two months later, I'm still have trouble swallowing the UJS kool-aid. @jamis

Funny how the JS people get all shocked and dismayed when jamis complains about UJS. Evidence of kool-aid. @rjs

Unobtrusive JavaScript isn't a fad, it's how you should write JavaScript. If you read this blog, you're probably doing it already.
If you have server-side developer friends who don't write much
client-side code, please send them this article!

I don't advocate blindly applying the techniques championed by the
proponents of unobtrusive JavaScript. However, I have learned lessons
from several hard-won battles which have enabled me to write JavaScript
more productively -- my own practices have evolved towards unobtrusive
JavaScript over the last 4-5 years.

Inline JavaScript

Where does inline JavaScript come from? One common place is server-side
frameworks. It's easier for frameworks to inject JavaScript through
helpers -- popular frameworks like Rails do
this. This is because the HTML and JavaScript these frameworks generate
is naturally tightly coupled. It's also because the framework builders
often try to appeal to the host language rather than seasoned JavaScript

Inline JavaScript crops up in several places:

These techniques work well for frameworks and make life easy for the
host language developer. In fact, Rails is one of the projects that
helped JavaScript frameworks bring Ajax to the world, and also the now
ubiquitous use of visual effects through projects like

The Alternative

The alternative isn't just about moving your JavaScript into its own
files. It's to change the way you think about JavaScript, the DOM, and
HTML templates.

If you've worked with CSS, you'll be familiar with the separation of
style from structure through the use of stylesheets. The popular
argument for writing unobtrusive JavaScript is that scripts define a
behaviour layer, and therefore should be separated, just like stylesheets.

This is true up to a point, but it takes a leap of faith to get there.
The key is to stop thinking about templates like static blocks of HTML,
and see documents for what they are: living, breathing tree structures
of DOM objects, events, and styles.


The main concern of those who are uncomfortable with writing unobtrusive
JavaScript is maintainability. This is deeper than just finding code, or
the link between a template and a script -- it also affects project

When planning new features, it can be difficult to determine what page
elements will be affected by a change if all of the JavaScript is
outside the templates.

Or is it?

Mental Models

People resist writing JavaScript this way is because their mental model
of a HTML document is based on HTML templates. A more useful mental
model is one which encapsulates the true nature of the DOM -- a document
is a collection of interrelated objects, affected by scripts,
stylesheets and their very structure.

When I start work on a new project or feature, I think in terms of these
objects and how they're related. I try to group like items and
behaviours -- just as styles can be efficiently shared, events can

Strategies and Patterns

DOM Ready

This is what makes healthy client-side code possible. DOM ready lets you
run a function when the document is in a usable state. This allows you
to safely bind event handlers. In jQuery it looks like this:

$(document).ready(function() {
  // Your code here


document.observe("dom:loaded", function() {
  // Your code here

This isn't actually an easy thing to write from scratch -- let the
framework do the work. I've written my own DOM ready handler before and
it wasn't much fun (mainly due to IE).

Event Delegation: DRY

If you've come to JavaScript through jQuery, you might be wondering what
I've been talking about. People raised on jQuery think in terms of event
delegation, and this is one of the key strategies to writing unobtrusive

Event delegation is where an event is bound to a containing element,
then conditionally executed based on the event's element.

Imagine if you wanted to open popup windows when the user clicks a link.
You could attach an onclick attribute and set up a window.
Every link would then have to repeat this code.

An alternative approach is to use event delegation to bind an event to a
high level element, the use a class to trigger a special popup event

$(document).click(function(event) {
  if ($(event.target).is('.popup') {
    // Open a new window with specified parameters

The benefit is similar to the benefits brought in by stylesheets -- you
can now change the behaviour of all popup windows across your entire
project. You could even have multiple class names for different window
properties -- sizes, toolbar hiding, whatever! It also makes the page
size smaller, all for the price of one event handler with an
if statement.

Internal Relationships

On larger projects I use a technique that maps controllers to
JavaScript objects. I do this by using body ID tags and
classes. A function runs when the DOM is ready and calls the appropriate

For example, let's say you're making a blog application in Rails (or
similar) and you have an ArticlesController. I'd use a
helper in my Rails layout to create an appropriate body tag:

<%= body :id => controller.name %>

Which is defined like this:

def body(options = {})
  tag 'body', options, true

Then my JavaScript delegation function would look at the body tag and
call a method on an associated object:

ArticlesController = {
  run: function() {
    // Do stuff that the articles templates need

$(document).ready(function() {
  var controllerName = '';
  if ($('body').id) {
    controllerName = $('body').id + 'Controller';
    if (typeof controllerName !== 'undefined') {

This body ID delegation pattern has allowed me to build very large Rails
projects without feeling lost when moving between JavaScript and Rails
templates. I also use body class names for shared functionality. The
JavaScript "controllers" are generally stubs that invoke other objects.

Any framework could use this technique -- it's really just an agreement
between your server-side code and JavaScript.

Test First

When I start writing JavaScript for a project, I try to think in terms
of the core functionality, independent of the browser. I'll write
console-based code and run unit tests in the console. Once I'm happy
with my "models" I'll start to hook up event handlers.

Get used to thinking about events through CSS classes -- try to group
like events and rely on high-level delegated event handlers. Not only
does this result in fast UI code, it also makes testing easier.

Light Event Handlers

Good code us usually easy to test. I've found that writing light event
handlers then calling higher-level objects helps keep testing simple and
increases reusability.


It's useful to be fully-versed in JavaScript's object model and
prototypal inheritance. Even though you can use certain libraries to ape
classical OO, good code comes from a deep understanding of the tools at

Maintainable and clear code comes from knowing when to exploit
JavaScript's flexible object system, rather than trying to write in a
style that doesn't suit it.

Byte-sized Takeaway

Keeping JavaScript out of templates can help:

To ensure projects are maintainable: