Recent Weblogs

Links I like

Event Dispatcher

Custom events is a need for many applications and since the whole RIA revolution that need is getting satisfied in many ways. I have looked at those who have come before me in this battlefield and I have deemed the model found in Action Script to be favorable.

ActionScript's interface

ActionScript defined four public methods for an event dispatcher class. All of which are identical in the EventDispatcher class written for javascript which can be found below.

  • addEventListener(type, listener) this method allows you to add a listener to the type of event. Type being the name of the event you want to observe, ie "click".
  • dispatchEvent(type) this method, typically internally initiated is the "trigger" method to fire the event to the observer array.
  • removeEventListener(type, listener) a method to remove the listener, the listener must be the exact reference that was sent, be careful when using "bind()".
  • hasEventListener(type) ensures that the listenerChain has a value for the specific event type.

By leveraging an already proven model from ActionScript and prototype.js' ability to mimic class behavior, including abstract class behavior we can create a base class from which concrete classes can extend from. In the code below I have attempted to avoid an initialize function to keep things "abstract". By doing this the implementation doesn't need to worry about initializing anything.

The Code


/**
 * @author 		Matthew Foster
 * @date		June 6th 2007
 * @purpose		To have a base class to extend subclasses from to inherit event dispatching functionality.
 * @procedure	Use a hash of event "types" that will contain an array of functions to execute.  The logic is if any function explicitally returns false the chain will halt execution.
 */
 var EventDispatcher = function(){};
     
     
     Object.extend(EventDispatcher.prototype,
                         {
                              
                              buildListenerChain : function(){
                                   
                                   if(!this.listenerChain)
                                        this.listenerChain = {};                                   
                              
                              },
                              addEventListener : function(type, listener){
                                   
                                   if(!listener instanceof Function)
                                        throw { message : "Listener isn't a function" };
                                   
                                   this.buildListenerChain();
                                   
                                   
                                   
                                   if(!this.listenerChain[type])                         
                                        this.listenerChain[type] = [listener];
                                   else
                                        this.listenerChain[type].push(listener);
                                   
                              },
                              hasEventListener : function(type){
                                   
                                   return (typeof this.listenerChain[type] != "undefined");
                              
                              },
                              removeEventListener : function(type, listener){
                                   if(!this.hasEventListener(type))
                                        return false;
                                        
                                   for(var i = 0; i < this.listenerChain[type].length; i++)
                                        if(this.listenerChain[type][i] == listener)
                                             this.listenerChain.splice(i, 1);
                              
                              },
                              dispatchEvent : function(type, args){
                                   this.buildListenerChain();
                                   
                                   if(!this.hasEventListener(type))
                                        return false;
                                        
                                   this.listenerChain[type].any(function(f){ return (f(args) == false ? true : false); });
                              
                              }
                         }
                         
                    );
					
					
Check out how I built a great Grid API with this class.

Event Propogation

You can stop the propogation of the event through the event chain by explicitly returning false from any of the listeners.

Argument(s)

The dispatchEvent method only accepts one variable as the "arguments" for the listening function. The idea behind this is either bundle your information into an object (Like an Event object with target and currentTarget properties) or you can bundle a series of objects into an array, then using apply you can resituate the variables for a function expecting parameterized arguments.

Other information

Comments are Disabled.