Simple Javascript Custom Event Object

Custom events are a fact of life. The Observer Pattern is indespensable. YUI supplies a custom event object - but to get it, you’ve got to include about 8 kilobytes of library in your project. Granted, YUI supplies much, much more than just an event object - but sometimes you don’t need all that. You just need a way for objects to talk to each other. Introducing a bare-bones event object that’s good enough for most situations! It’s not as flexible as YUI’s, but it gets the job done.

First, we need to define the object. We know it’s going to need a list of subscribers, so let’s start there.

  1. var Event = function() {
  2.   var subscribers = [];
  3. };

This isn’t going to do much. You can’t even access the subscriber list! Let’s put some meat on this bone.

  1. var Event = function() {
  2.   var subscribers = [];
  3.   this.getSubscribers = function() {
  4.     return subscribers;
  5.   };
  6.   this.addSubscriber = function(fn) {
  7.     if(typeof fn === "function") {
  8.       subscribers.push(fn);
  9.     }
  10.   };
  11. };

Now we’re cooking. We can add functions to our list - but we can’t really do much with them! Let’s fire things up.

  1. var Event = function() {
  2.   var subscribers = [];
  3.   this.getSubscribers = function() {
  4.     return subscribers;
  5.   };
  6.   this.addSubscriber = function(fn) {
  7.     if(typeof fn === "function") {
  8.       subscribers.push(fn);
  9.     }
  10.   };
  11.   this.fire = function() {
  12.     for(var i = 0; i < subscribers.length; i++) {
  13.       subscribers[i]();
  14.     }
  15.   };
  16. };

We can add subscribers and call them now. But their scope isn’t quite what we want it to be. Let’s add the ability to define a scope for any subscriber to run in as a default - and then let the subscriber choose whether it would rather run in the default event scope or in one of its own.

  1. var Event = function(defaultScope) {
  2.   var subscribers = [];
  3.   this.getSubscribers = function() {
  4.     return subscribers;
  5.   };
  6.   this.addSubscriber = function(func,scope) {
  7.     if(typeof func === "function") {
  8.       subscribers.push({
  9.         fn : func,
  10.         sc : scope
  11.       });
  12.     }
  13.   };
  14.   this.fire = function() {
  15.     var scope = null;
  16.     for(var i = 0; i < subscribers.length; i++) {
  17.       scope = subscribers[i].sc || defaultScope;
  18.       subscribers[i].fn.call(scope);
  19.     }
  20.   };
  21. };

This looks a little better. We can define a default subscriber scope when we instantiate a new Event object. If a subscriber so wishes, they can override this when they subscribe. Note the use of closures to keep the defaultScope variable private. You don’t want people messing with this variable directly - subscribers may be depending on the scope to be what they expect it to be! Note also that the subscribers array is private - you don’t want just anybody to be able to come along and wipe out all the subscribers — or add junk subscribers on their own! It’s still not great though: there’s no way to pass data into the subscribers when you fire the event. Pretty easy, though!

  1. var Event = function(defaultScope) {
  2.   var subscribers = [];
  3.   this.getSubscribers = function() {
  4.     return subscribers;
  5.   };
  6.   this.addSubscriber = function(func,scope) {
  7.     if(typeof func === "function") {
  8.       subscribers.push({
  9.         fn : func,
  10.         sc : scope
  11.       });
  12.     }
  13.   };
  14.   this.removeSubscriber = function(func) {
  15.     for(var i = 0; i < subscribers.length; i++) {
  16.       if(subscribers[i] === func) {
  17.         subscribers.slice(i,1);
  18.         return true;
  19.       }
  20.     }
  21.     return false;
  22.   };
  23.   this.fire = function(data) {
  24.     var scope = null;
  25.     for(var i = 0; i < subscribers.length; i++) {
  26.       scope = subscribers[i].sc || defaultScope;
  27.       subscribers[i].fn.call(scope,data);
  28.     }
  29.   };
  30. };

I cheated a little bit and added a pretty self-explanatory removeSubscriber method in there. But there it is in a nutshell!

Reblog this post [with Zemanta]

Tags: , , , , ,

Leave a Reply