November 26, 2010

Javascript Event Model

A while ago I was working on a proof of concept for a mobile web application, and I realized that despite my use of a well-known JS framework I needed to create a few custom pieces to get all of the functionality I needed into the app.

One of the custom pieces I needed to create was an event model for my various custom objects to use during the application lifecycle, to broadcast changes in things such as network status.

Creating the event object itself was easy enough:

Event = function(config) {

     this.config = config;
     this.name = config.name;
     this.subscribers = [];

     this.attachListener = function(scope,func) {
            this.subscribers.push({s:scope,f:func});
     };

     this.dispatchEvent = function(sender, args) {
            for(i=0;i<this.subscribers.length;i++){
                 var callback = this.subscribers[i];
                 callback.f.apply(callback.s,arguments);
           }
     };
}


An event object takes a config object as its only parameter. The minimum requirement for the config object is that it have a name property, however it can obviously carry with it other information as well. The attachListener function does what you might expect: it takes an object for scope and a function of the scoped object to be used as a callback. Finally, the dispatchEvent function calls the callback method for each subscriber, within the scope specified by the subscriber.

Other javascript objects can utilize the Event object to dispatch events based on changes in state. Here's a simple example of one such object:

MyEventCaller = function() {

      this.myEvent = new Event({name:"myEvent"});

      this.callMyEvent = function(msg){
            this.myEvent.dispatchEvent(this,{message:msg});
      };
}


The MyEventCaller object has one property, myEvent, and one method, callMyEvent, which dispatches an event with a custom message. Now to complete the model, all we need is a listener:

MyEventListener = function() {
       this.onMyEvent = function(sender, args){
             alert(args.message);
       };
}


Now that we have our event, caller, and listener objects set up, we can write a script that uses them:

var caller = new MyEventCaller();
var listener = new MyEventListener();

caller.myEvent.attachListener(listener, listener.onMyEvent);

caller.callMyEvent("Hello world!");


So what happens here is that we've created an instance of our caller and our listener, and we've attached the listener's onMyEvent function to the caller's myEvent object. In the final line we've called the caller's callMyEvent function with a message. The callMyEvent function dispatches myEvent, which calls the callback functions that have registered to listen to it. As a result, we have this:


Of course, we can just as easily do away with our listener object and attach a listener function to our caller inline:

var caller = new MyEventCaller();

caller.myEvent.attachListener(this, function(sender, args){alert(args.message);});
caller.callMyEvent("Hello inline world!");


And, as expected, we get the following result:


I hope that you find this event model helpful for creating custom events and listening for them from other objects. As always, please feel free to leave comments or questions!
Post a Comment