Tough Love: How Steve could make us all like Flash more JavaScript 2: A Perl 6 disaster, that matters so much more, but wait…
Aug 06

Enjoying the Observer pattern with custom events

Ajax, Tech with tags: , , Add comments

I created an introductory example discussing custom events as an implementation of the Observer pattern.

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.

UPDATE: We now have Prototype, jQuery, DOMAssistant, and Appcelerator versions. Thanks Malte!

26 Responses to “Enjoying the Observer pattern with custom events”

  1. Matthew Quinlan Says:

    It’s funny how we had this custom event model back in the days of client-server (Swing, Powerbuilder, VB, Win32) but totally lost it when the web took over (no, OnClick isn’t enough). We took this same approach when designing the message-oriented architecture inside Appcelerator (except we try to enable observation declaratively, without JavaScript, where possible).

    What’s get’s really interesting is where you allow observers to be sitting on the both the client and the SERVER side and start naming your events according to the intended functionality rather than according to the specific HTML control. Maybe we’ll port your example to Appcelerator and post it back.

    Cheers!
    -Quin’

  2. dion Says:

    Matthew,

    Very cool indeed. I do hope that you post an example, and I will put it up in the SVN repo to share.

    Cheers,

    Dion

  3. Malte Says:

    Hey Dion,

    here one-to-one port of your example using jQuery
    http://joose-js.blogspot.com/2008/08/custom-events-in-jquery.html

    Cheers
    Malte

  4. dion Says:

    Awesome! I put it up side by side the other one:

    http://google-ajax-examples.googlecode.com/svn/trunk/customevents/jquery.html

    Cheers,

    Dion

  5. Malte Says:

    Thanks,

    by the way (please excuse my dump question): Do you need special google super powers to deliver non text/plain mime types from *.googlecode.com or is that just a checkbox somewhere in the Google Code admin?

    Did you see my email about blok? http://blok.appspot.com/?id=dion

    Bye
    Malte

  6. dion Says:

    Malte,

    Nothing magical to turn on content type goodness. You just need to set the mime type. E.g.

    % svn propset svn:mime-type text/html *.html

    I will check out blok!

    D

  7. Kevin Whinnery Says:

    I implemented this page using Appcelerator (http://www.appcelerator.org) and it’s nifty Web Expression Language. You know I hear there’s a book coming out about it (http://www.manning.com/whinnery)... ;)

    Check it out here:

    http://winnerbyfall.appspot.com/test.html

  8. Malte Says:

    Ah, nice, svn mime types.

    Just open blok in two browser windows or two computers to see the magic :)

  9. chenghong Says:

    Hi Dion,

    Here’s a DOMAssistant port: http://www.domassistant.com/demos/customevents.htm

    Cheers,
    chenghong

  10. Matthew Quinlan Says:

    One of the Appcelerator community members has ported it to appcelerator using 2 lines of JavaScript! :) He will be posting it here shortly.

  11. Dion Almaer Says:

    chenghong,

    Awesome! Thanks so much. I put this up side by side: http://google-ajax-examples.googlecode.com/svn/trunk/customevents/domassistant.html

    Cheers,

    Dion

  12. Matthew Quinlan Says:

    Here’s Kevin’s Appcelerator port…nice work Kev!

    http://winnerbyfall.appspot.com/test.html

    Notice almost zero javascript!

  13. Dion Almaer Says:

    Matthew and Kevin,

    Awesome! Put it up again side by side:

    http://google-ajax-examples.googlecode.com/svn/trunk/customevents/appcelerator.html

    Cheers,

    Dion

  14. Ivo Beckers Says:

    Here’s another Appcelerator / SOA approach and JS oneliner:

    http://ibec.appspot.com/events.html

    “Expressions” are used on the sending side to decide whether or not an event (i.e. “message”) should be fired. As such, no add/remove events are necessary. And the behaviour of an HTML-object is defined within the object itself, not in a JS function.

    cheers.

  15. Matthew Smith Says:

    I’ve created a MooTools version 1.2 for you.

    http://www.800px.com/MooTools_Custom_Events.html

    One thing to point out about your original demo and the others including this one – if I’m correct, they are don’t work in the same way!?

    In your original example you seem to make use of Event propagation by attaching your “observe” methods to the document and the “fire” method to the actual element that fires the method. Does this Mean that it could be picked up by a series of elements higher in the DOM tree, which would know which element was originally fired?

    I don’t really know too much about the other different frameworks but in MooTools the “fire” method stops propagation within the MooTools Core code; meaning that every method done in this way has to fire events on other elements directly. Thus we have to pass the actual fired event to the document event using the “fire” methods second argument, this will bypass any observers on any of the the elements higher in the DOM tree.

  16. dion Says:

    Ivo,

    Very cool. It is interesting to compare and contrast approaches with the same library.

    For some reason when I run your example I am getting:

    “Element [div] ith ID: app_14 has an exception:
    message: console is not defined, location: 24, stack: (”do.quote”,[object Object],”JSON”,”local”,”appcelerator”)@http://ibec.appspot.com/javascripts/appcelerator.js:24 (”do.quote”,[object Object],”JSON”,”local”,”appcelerator”)@http://ibec.appspot.com/javascripts/appcelerator.js:21 ([object Object],”do.quote”,[object Object],”JSON”,”local”,”appcelerator”,”1.0″)@http://ibec.appspot.com/javascripts/appcelerator.js:21 (3)@http://ibec.appspot.com/javascripts/appcelerator.js:21 (3)@http://ibec.appspot.com/javascripts/appcelerator.js:21
    in unknown”

    You?

    Cheers,

    Dion

  17. dion Says:

    Matthew,

    Thanks, I have put it up at http://google-ajax-examples.googlecode.com/svn/trunk/customevents/mootools.html

    Very good point about event propagation too.

    Cheers,

    Dion

  18. Ivo Beckers Says:

    Dion,

    The “console” messages were caused by some debug/trace code. It should be fixed now:

    http://ibec.appspot.com/events.html

  19. curtis Says:

    hey Dion,
    i have a couple of friend over in the olympics and i want to display the photos from facebook on to out blog? how did u do it on you site?
    is it a plugin for you blog?
    i add the photo2rss on my facebook. i just want to pull this to my blog?

    email me back? please?

    Curtis Z

  20. Techno Cake Says:

    it’s nice code, i’ll put on my own

  21. Matthew Quinlan Says:

    Nice work Ivo! I like the revision :)

  22. Ivo Beckers Says:

    Dion,

    Some Appcelerator parts (configuration, widgets) are missing in your customevents directories. That’s the reason why the appcelerator2 demo fails.

    Plus, do you plan to add Appcelerator (the web SDK part) to http://code.google.com/apis/ajaxlibs/ ?

    -Ivo

  23. Dave Johnson Says:

    Sort of a tangent Dion but a useful feature that we have come up with in our Observer pattern implementation is an observeOnce method that will make it so that the event only gets fired once and then auto detaches itself. Very handy for page load situations or dynamically chaining events.

    -dave

  24. dion Says:

    Dave,

    Ah, very interesting. Fancy writing up the use cases for that as a guest post on Ajaxian? ;)

    D

  25. asanga bandara wijekoon Says:

    Here more information about the observer pattern – http://asanga86.wordpress.com/2009/05/30/observer-pattern/

  26. Shawn D Says:

    Hey Dion,

    Ajaxian linked to this post in one of their posts today. I did see a mootools version but it didn’t work for me so I humbily took a stab at it:

    http://www.shawndube.com/CustomEvents-Mootools.html

    S

Leave a Reply

Spam is a pain, I am sorry to have to do this to you, but can you answer the question below?

Q: What is the number before 3? (just put in the digit)