Javascript Behaviors

This page presents a library of Dynamic HTML behaviors, modular units of Javascript code that can be associated with elements in a web page chosen with CSS selectors.

The library is based on Ben Nolan's Behaviour.js, but with a slightly different design. The library of CSS selectors was written by Simon Willison.

I'm releasing this under the LGPL. Ben's original library is released under a BSD license. Both of our libraries rely on Simon's getElementsBySelector function, which doesn't seem to be under any license.

HTML and Javascript: Don't Cross the Streams [1]

Dynamic HTML (DHTML) enables programmers to use Javascript to modify the style, contents, or interactive behavior of the elements of a web page. In particular, programmers can attach event handlers to document elements, which respond to user actions with programmable behavior. The easiest way to do this is to specify an event handler property directly in the HTML:

<div id="foo" onclick="alert('clicked!')">...</div>

As proponents of unobtrusive Javascript point out, there are problems with writing web pages this way. For one thing, it mingles two unrelated pieces of the web page: the document's structure (represented by the HTML code), and its behavior (represented by the Javascript). It makes it difficult to split the task among different developers, say, a programmer and a graphic designer. And it results in a monolithic structure that's hard to understand and maintain. Aspect-oriented programmers call this the problem of tangling.

Another problem with this code is that such web pages end up with bits of conceptually related Javascript code strewn about the web page. For example, if all div elements with the class clickable are supposed to respond to the onclick event in the same way, the programmer shouldn't have to copy the same code in the onclick property of each such element in the web page. Aspect-oriented programmers call this problem scattering.

A Non-Solution

The alternative, of course, is to move this logic into one place in the document's initialization code. Unfortunately, in practice this usually means writing code to select each relevant node, by searching the DOM tree or chasing child and parent pointers, and set its corresponding properties.

var nodes = document.getElementsByTagId('div');
for (var i = 0; i < nodes.length; i++) {
    if (nodes[i].className == 'clickable') {
        nodes[i].onclick = function() { alert('clicked!'); };
    }
}

While this approach solves the problems of scattering and tangling, the code is bloated and hard to read. Worse, it can be brittle: the hand-written code for selecting nodes can be fragile, depending on the specific layout of the document, which tends to change often. [2]

A Better Way

A better solution than all this searching and pointer-chasing is to use a higher-level language for selecting DOM nodes. Lucky for us the W3C has already put a lot of work into designing such a language! With CSS selectors, we can describe collections of elements in a web page according to the high-level design of the document's structure.

For example, we can create a rule set that registers an onclick handler for all nodes of class clickable:

var myrules = {
    '.clickable' : {
        onclick : function() { alert('clicked!'); }
    }
};

Behavior.register(myrules);

Now we can add, delete, and move nodes from the entire web page with the clickable class, and our event handler code remains unchanged.

Rule Composition

One of the benefits of this declarative style of web programming is that the library can take responsibility for the semantics of combining multiple rules. This means we can combine multiple event handlers simply by registering them with Behavior.register.

TODO: more to come...

For More Information...


  1. Venkman: I'm fuzzy on the whole good/bad thing. What do you mean, "bad?"
    Spengler: Try to imagine all life as you know it stopping instantaneously and every molecule in your body exploding at the speed of light.
    Stantz: Total protonic reversal.
    Venkman: That's bad. Okay. All right, important safety tip, thanks Egon.
  2. Did you notice the subtle bug here? Instead of testing the element's className for equality to 'clickable', we should have checked that it included the class name, since HTML elements can have multiple classes separated by whitespace. This is one of several kinds of bugs that are easy to make when manually searching the DOM.