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.
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.
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 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.
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...
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.