Cleaning your markup with dojo.behavior
by Robert Coup

CSS separates our formatting from our content. But we’re still left with onclick="..." attributes sprinkled throughout our otherwise pristine markup - not exactly “unobtrusive Javascript”. Dojo.behavior comes to the rescue, providing a nice separation between our content and the code associated with it.

Traditionally we use inline event handlers:

<ul>
<li onclick="if(confirm('Are you sure?')) {this.parentNode.removeChild(this);}"><img src="/images/100100.png" /></li>
<li onclick="if(confirm('Are you sure?')) {this.parentNode.removeChild(this);}"><img src="/images/100101.png" /></li>
</ul>

Dojo.behavior allows us to use clean markup:

<ul id="imageList">
<li><img src="/images/100100.png" /></li>
<li><img src="/images/100101.png" /></li>
</ul>

Combined with a single, concise piece of Javascript to add the functionality:

dojo.behavior.add({
    '#imageList li': {
        onclick: function(evt) {
            if (confirm('Are you sure?')) {
                this.parentNode.removeChild(this);
            }
        }
    }
});

Ben Nolan came up with the Behaviour Javascript library in 2005 based on recommendations by PPK and Simon Willison about unobtrusive Javascript. Dojo.behavior brings Ben’s library to Dojo, with the power of dojo.query() (and US spelling). The behaviour code is in Dojo Core, so we just need to do dojo.require("dojo.behavior") to get started.

At their simplest, behaviours consist of a CSS selector and some code. The selector can use classes, IDs, node types or anything else supported by dojo.query(). On the code side, there are a few different configurations of behaviours, which are outlined below.

Running code for each node:

dojo.behavior.add({
    '.foo' : {
        found: function(node) {
            // do something with each node found
        }
    },
 
    // shortcut for found
    '.foo2' : function(node) {
        // do something with the node
    }
});

Connecting to DOM events (using dojo.connect() underneath):

dojo.behavior.add({
    '#my li': {
        onclick: function(evt) {
            alert('click!');
        },
        onmouseover: function(evt) {
            alert('mouseover!');
        }
    }
});

Publishing topics (using dojo.publish() underneath):

dojo.behavior.add({
    '.topic1': {
        found: '/foo/t1/found',
        onclick: '/foo/t1/click'
    },
 
    // shortcut for found
    '.topic2': '/foo/t2/found'
});

Dojo.behavior consists of two functions - add() and apply(). You can register as many behaviours as you need to with add(), then call apply() to take all the registered behaviours and apply them to the nodes. When the page is loaded apply() is automatically called once for you, so normally you just need one or more add() calls.

Next, we can pull all our behaviours into a separate Javascript file. Once we’ve done this we have clear separation between the content (HTML markup), presentation (in a CSS file), and behaviour (in a JS file). Which makes the designers and the coders much happier.

Tags: , ,

This entry was posted on Wednesday, March 26th, 2008 at 12:00 pm and is filed under Dojo Cookies. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

8 Responses to “Cleaning your markup with dojo.behavior”

  1. seth Says:

    Nice article.

    What are the advantages of using dojo.behavior, vs. dojo.query.connect or dojo.connect? Centralized location of code?

  2. Robert Coup Says:

    Hi Seth,

    Thanks!

    There’s no technical difference between them. It really appeals to me because it’s clean - at a glance i can see what behaviours are applying to which nodes - no addOnLoad() blocks full of repeated calls to dojo.query(). In one app we have several modules that might combine on a page - each has a behaviours section with a call to dojo.behavior.add() in it - you know where to look, and its not mixed up with other onLoad code.

    Rob :)

  3. seth Says:

    So, I gave dojo.behavior a whirl. I tried to set up a keyboard command to make something happen, so I did this:

    dojo.require(”dojo.behavior”);
    dojo.behavior.add( {
    body: {
    onkeypress: function(evt) { console.log(”ur doin it wrong”) }
    }
    });
    dojo.behavior.apply();

    That didn’t do anything unless I click in a text box and start typing. I expteced the event to fire if I clicked anywhere on hit the keyboard command.

    Yet, when I do this:
    dojo.connect(document.getElementsByName(”body”)[0],
    “onkeypress”,
    function(evt) {
    console.log(”o hai!”);
    }
    );

    and click anywhere on the page (like on an empty div) and start typing, it’ll output ‘o hai’

    I realize this might not be the place to post this but I figured I’d stop here before I dove into the forums.

    Thanks in advance.

  4. seth Says:

    Speaking of doing it wrong, I had buggy code….should have been *document.getElementsByTagName(”body”)*

    Now it behaves the same as dojo.behavior. Anyway to use dojo.behavior so it binds to the whole page? :)

  5. Robert Coup Says:

    Hi Seth,

    Adding behaviours to body seems to work for me - dojo.behavior applies its queries to the document so everything should fall into its scope.

    > dojo.behavior.add({ ‘body’ : { onkeypress:function(e) { console.debug(’o hai’, e); } } });
    > dojo.behavior.apply();

    In FF2/OSX keys pressed with no element focussed ran the auto-search functionality instead, but it worked fine in a textarea on the page.

    - Rob :)

  6. seth Says:

    strange…it doesn’t work for me on FF2/Ubuntu 7.10 (unless my focus is in a text box or text area). when I add a found event, it finds it.

    >> dojo.require(”dojo.behavior”);
    >> dojo.behavior.add({ ‘body’ : { found: function(e) { console.debug(e); }, onkeypress:function(e) { console.debug(’o hai’, e); } } });
    >> dojo.behavior.apply();

    Thanks for the reply….

  7. pottedmeat Says:

    @Seth, there’s a major difference. dojo.behavior manages the application of the behaviors.

    So if you add a node to the document, then use apply, it won’t re-apply the behavior to nodes that have already been matched.

  8. SitePen Blog » Simplifying Maintenance With Event Driven Design Says:

    [...] been using dojo.behavior (see also here) quite a bit lately. As I mentioned in the post introducing Queued, we used it for [...]

Leave a Reply

You must be logged in to post a comment.