Friday, May 4, 2012

JavaScript Fun Part 5: Build Your Own Custom Events

Custom Events And JavaScript

A few times in my life I've been asked "How do I make my own events in JavaScript?". Generally I tell them to just use JQuery or any number of libraries that have already solved this problem. But for sake of argument, let's say you wanted to implement something like this yourself. The problem at hand is JavaScript doesn't have anything OOTB that handles event binding and triggering. DOM does, of course, but DOM isn't JavaScript (it's just that slow P.O.S. that JavaScript is usually manipulating).

So how do we go about implementing a rudimentary JavaScript event library that will enable us to bind to our own custom events and trigger them? First, we'll want to set up the "guts" of this functionality with two methods: A bind method and a trigger method.

/* Create a namespace for us to work in
* because I'm lazy, I'll call it "E" */
var E = E || {};

/* Our bind method: this accepts a target, which is
* the object we're going to attach our event to, an eventname,
* which serves as a key for the events we put on the class, and
* of course, the handler, which is the function that will be 
* called when the event is triggered. */
E.bind = function(target, eventname, handler) {
    if (!target.customevents) {
        target.customevents = {};
    }
    if (!target.customevents[eventname]) {
        target.customevents[eventname] = [];
    }
    target.customevents[eventname].push(handler);
};
/* The trigger method: this accepts a target, which is the
* object containing the event to be triggered, the eventname,
* which is the key for the collection of handlers to be 
* called, and args, which are the arguments to pass
* to the handlers. */
E.trigger = function(target, eventname, args) {
    if (target.customevents && target.customevents[eventname]) {
        var handlers = target.customevents[eventname];
        for (var i = 0; i < handlers.length; i++) {
            var handler = handlers[i];
            handler.apply(handler, args);
        }
    }
}

//create an object to bind events to.
var myObject = {};

//bind a couple of handlers to the "testevent".
E.bind(myObject, 'testevent', function(a, b) {
   alert('called! ' + a + ', ' + b); 
});

E.bind(myObject, 'testevent', function(a, b) {
   alert('Say what? ' + a + ', ' + b); 
});

//trigger the "testevent".
E.trigger(myObject, 'testevent', ['FOO', 'BAR']);
//called! FOO, BAR
//Say what? FOO, BAR

What's going on above is pretty simple. Basically, our bind method takes a function reference and puts it in a collection of arrays keyed by the eventname (in our test case, "testevent"). Then our trigger method, when called, gets all of the handler function references on the object keyed by the event name ("testevent" again), and calls them. There's a little trickery in there with the JavaScript apply() method. Basically, apply(scope, arr) is just a way of passing an array of values as arguments to a function within a specified scope. The scope in this case is mostly irrelevant.

So how do we make this prettier? I don't like magic strings!

Okay so let's use our E namespace in a way that is a little cleaner, something like how JQuery uses events, perhaps?

/* create a class with a method to call that will
* trigger a custom event, and a method to use to bind 
* the that custom event. It's a static class 
* because, again, I'm lazy. */
var Foo = {
    bar: function(a, b) {
        E.trigger(this, 'bar', [a, b]);
    },
    onBar: function(handler) {
        E.bind(this, 'bar', function(a, b) {
            handler(a, b);
        });
    }
};

Now we use our prettier methods in whatever code consumes this class.
// bind to the event.
Foo.onBar(function(a, b) {
   alert('Foo.bar called! ' + a + ', ' + b); 
});

// call bar() and trigger the event!
Foo.bar('TEST', 'MONKEY');

That's really all there is to it. Of course, as I said above, there really isn't a good reason you should be implementing your own custom event library, when there are already so many powerful and refined libraries out there that tackle this problem, such as JQuery.

No comments:

Post a Comment

This form allows some basic HTML. It will only create links if you wrap the URL in an anchor tag (Sorry, it's the Blogger default)