Thursday, August 7th, 2008

Enjoying the Observer pattern with custom events

Category: JavaScript

I created an introductory example discussing custom events as an implementation of the Observer pattern. I posted this on my personal blog first and quickly got a port from my Prototype version to Malte’s jQuery port (and now we have a DOMAssistant and Appcelerator versions). I hope others keep them coming so we can aggregate a simple example of custom events and how it works across various toolkits:

The example dynamically adds functionality based on checkboxes to simulate events that could change processing and uses the fire() and observe() methods from Prototype.

I strap on behaviour to a list of names. When you click on the name, something can happen. To do this I could have done the following:

$$('ul#leftchoices li').each(function(el) {
    el.observe('click', function(e) {        
        if ($('colorchange').checked) {
           changeColor();
        }

        if ($('contentchange').checked) {
           changeContent(el.id);
        }
    });
});

In the click event itself, I do various checks and kick off behaviour. That can be fine for small actions, but what if you want to do more? This is when I prefer to abstract out the action and just fire an event:

$$('ul#leftchoices li').each(function(el) {
    el.observe('click', function(e) {
        el.fire('selected:choice');
    });
});

At this point, this bit of code becomes dumb. It doesn’t know what to do when you select the item, and it just hopes that somewhere, someone is listening. That is where the observers come in, such as this one that changes the main content based on the selected name:

$('contentchange').onchange = function(e) {
    if (e.target.checked) {
        document.observe('selected:choice', changeContent);
    } else {
        document.stopObserving('selected:choice', changeContent);
    }
}

When someone clicks on the checkbox, this method is fired and an observer is either added, or taken away.

Once you start building applications with this in mind, you may find a bit of a sea change. You start to think about the various events as a public API that you can easily expose to observers. Gone is the simple ability to look at one method and see what is happening, but the loose coupling gives you the ability to easily layer in your architecture. Based on some settings, behaviour can be dynamically added. You could even expose these events in a way that makes it easier for Greasemonkey hackers to come in and work with your application. All in all, a win-win for anything more than a simple example.

Although this example uses Prototype, you could do the same with the other top class JavaScript libraries. In fact, if someone wants to port this example to Dojo, jQuery, Mootools, YUI, or anything else, send me the files and I will put them up so we can all compare how custom events are done in the various toolkits.

Posted by Dion Almaer at 7:59 am
10 Comments

+++--
3.8 rating from 22 votes

10 Comments »

Comments feed TrackBack URI

This kind of pattern makes working with isolated JS components really nice. You separate your concerns, and are more easily able to test isolated parts of your code. We’ve been doing this for a while here at Motionbox and developed a library to help out with functionality like this ( http://github.com/tobowers/motionbox-eventhandler/tree/master ). Our custom events can also carry a payload with them (any data or objects you want). Our general theory is any important event should fire off an event (maybe in the global scope). Then, later you have a rich event ecosystem which you can build on top of.

Comment by TopperBowers — August 7, 2008

We have something similar in JS.Class — Observable is a JavaScript implementation of Ruby’s Observable module. It’s more general-purpose in that it allows any JavaScript object to fire events, not just DOM objects. This is what Ojay’s custom event system is based on.

http://jsclass.jcoglan.com/observable.html
http://ojay.othermedia.org/articles/observable.html

It’s always irked me slightly that we seem to get the Observer pattern so closely tied up with the DOM on the web, and I see many custom DOM events that don’t really belong in the DOM at all.

Comment by jcoglan — August 7, 2008

Ryan Johnson has had an implementation for quite a while now, built on prototype
http://livepipe.net/core

Dojo’s dojo.connect is a very similair concept too
http://dojotoolkit.org/book/dojo-book-0-9/part-3-programmatic-dijit-and-dojo/event-system/simple-connections-dojo-connect

Comment by DanF — August 7, 2008

The separating events into larger “observers” is a very nice idea, and I have started implementing it in my own projects now. However, it would be nice to see a link to some large projects that have implemented this on a larger scale so I can dig through the code, get some ideas and such. If anyone has some links to large projects, or advanced tutorials on the subject, I would love to see them. Thanks.

Comment by tj111 — August 7, 2008

http://www.two-birds.de is completely based on a complex timeout-interval-wait-observer stack to check for the availability of depending files for the on-demand loading mechanism. Its tb.observer.observe() function checks for changes in DOM or JS data elements, but lacks event hooks so far. I will add a twoBirds implementation of the page for comparision. BRB. :-)

Comment by FrankThuerigen — August 7, 2008

I ported this to Appcelerator (http://www.appcelerator.org) – a live example is posted at http://winnerbyfall.appspot.com/test.html. Check it out and make sure to view source it — Appcelerator’s Web Expression Language is a truly innovative approach to event-driven UI programming for the web.

Comment by kevinwhinnery — August 7, 2008

There’s nothing revolutionary about this. I was wondering what the hype was about so I ported it for MooTools: http://ibolmo.com/sandbox/events.html

It’s 1.2 compatible, but same strategy has been employed pre 1.0 days.

Comment by ibolmo — August 7, 2008

This time I galloped over the cliffs edge – normalizing my observer to handle events, DOM element attributes and nomal JS Objects in one function call will take some time… ;-)
I´ll be back though. My apologies for promising more than I can do on a short time.

Comment by FrankThuerigen — August 7, 2008

Kevin,

LOVE the fact that it has almost zero javascript! Nice work!

Comment by MattQuinlan — August 7, 2008

In attempt to show some code for setting up custom events for various libraries, in another project I’ve done, I used the adapter pattern for plugging in various libraries (dojo, jquery, mootools, prototype, yui), and in each adapter is an example of how to setup a custom event with that library. I was surprised how it varied from each library, but each worked well. You can see code examples in the source on google code:

http://code.google.com/p/slikcalc/source/browse/tags/slikcalc/1.0/slikcalc/src/adapters/?r=48

Comment by bradharris — August 7, 2008

Leave a comment

You must be logged in to post a comment.