Javascript Bubbling

Posted on September 23, 2009

While developing my new game I’ve been doing a lot of Javascript code. In my quest to keep everything modular and decoupled my javascript objects often delegates some kind of responsibility to child objects, which in turn might delegate the responsibility to their child objects forming a kind of tree structure. I’ve made sure that the child objects do not know anything about their parents to decouple even more. This is implemented via the observer/subscriber pattern. The child objects just send out a notification when a certain event happens and the parent receives this if they’ve subscribed to the sending child. This results in a very flexible decoupled design – yummy!

However, this design turned out to yield quite some code duplication which needed to be cleaned up.

Let me explain how the code duplication emerged. Often a grand grand grand child object sends out a notification that has to go through through several objects upwards in the tree, perhaps all the way to the root object. Since the children’s only way to communicate upwards in the tree is to send a notification, I found myself writing the following over and over again:

var GrandChild = Class.create({
  certainEvent: function(argument1, argument2) {
    this.notify('certainEvent', argument1, argument2);
  },
  anotherCertainEvent: function(argument1, argument2) {
    this.notify('anotherCertainEvent', argument1, argument2);
  }
  [..]
});
var Child = Class.create({
  certainEvent: function(argument1, argument2) {
    this.notify('certainEvent', argument1, argument2);
  },
  anotherCertainEvent: function(argument1, argument2) {
    this.notify('anotherCertainEvent', argument1, argument2);
  }
  [..]
});
var Parent = Class.create({
  certainEvent: function(argument1, argument2) {
    this.notify('certainEvent', argument1, argument2);
  },
  anotherCertainEvent: function(argument1, argument2) {
    this.notify('anotherCertainEvent', argument1, argument2);
  }
  [..]
});

The idea is that when GrandChild sends a notification, Child receives this and sends out another notification which Parent receives. This is repeated until some receiving parent object decides to not send it further upwards. After writing something like the above a dozen times I figured I had to clean it up, somehow; there was too much duplication going on. I ended up implementing a bubble method (as in DOM Event Bubbling) as a part of my Observable class:

var Observable = Class.create({
    subscribe: function(observer, event) {
        [...]
    },
    unsubscribe: function(observer, event) {
        [...]
    },
    notify: function() {
        [...]
    },
    bubble: function() {
        for(var i=0; arguments.length>i; i++) {
            var method_name = arguments[i];
            this[method_name] = this.bubbleMethod(method_name);
        }
    },
    bubbleMethod: function(method_name) {
        return function() {
            var passing_arguments = this.argumentsToArray(arguments);
            passing_arguments.unshift(method_name);
            this.notify.apply(this, passing_arguments);
        }.bind(this);
    },
    argumentsToArray: function(arguments, start_from) {
        start_from = start_from || 0;
        var array = [];
        for(var i=start_from; arguments.length>i; i++) {
            array.push(arguments[i]);
        }
        return array;
    }
});

With the help from the above bubble method, the example from before would now look like this:

var GrandChild = Class.create({
  initialize: function() {
    this.bubble('certainEvent', 'anotherCertainEvent');
  }
  [..]
});
var Child = Class.create({
  initialize: function() {
    this.bubble('certainEvent', 'anotherCertainEvent');
  }
  [..]
});
var Parent = Class.create({
  initialize: function() {
    this.bubble('certainEvent', 'anotherCertainEvent');
  }
  [..]
});

As you can see the duplication is reduced dramatically thus making the overall source code quite a bit smaller without comprising decoupling. Awesomeness! :D

Comments
  1. AnonymousOctober 06, 2009 @ 04:40 PM

    Wtf does all that meean? :S