As things currently stand, the vast majority of event handling between JavaScript and the DOM is somewhat primative; only involving the element being affected by the event. An example of this is a simple rollover effect:

$(elem).hover(function(){
    $(this).addClass('over');
}, function(){
    $(this).removeClass('over');
});

The only effect induced by the event is on the element itself. Even with something as simple as the above example there’s a lot of uneccessary bloat, all of which could be avoided by a simple abstraction; consider this plugin:

$.fn.hoverClass = function(className){
    return this.hover(function(){
        $(this).addClass(className);
    }, function(){
        $(this).removeClass(className);
    });
};
 
// Usage:
$(elem).hoverClass('over');

While that hugely simplifies the process it doesn’t provide much scope as a plugin; it can only be used for adding and removing a class when an element is hovered over. These kind of abstractions, as far as I’m concerned, are perfectly welcome; anything that makes the end product more readable is a bonus!

On the topic of readability, a thread came up a while ago on jQuery’s development group within which the benefits of “ultra-chaining” were discussed; a term first highlighted by John Resig in a blog post back in ’08. It’s definitely worth a read!

The resulting proposals were all of the same basic idea; provide methods to enable more seamless chaining in jQuery. Here’s an example:

// From the Dev thread:
jQuery("div").hide("slow") 
  .wait() 
  .addClass("done") 
  .find("span") 
    .addClass("done") 
  .end() 
  .show("slow") 
  .wait() 
  .removeClass("done")

As John stated in his post, this style, if adopted, would transform jQuery into a domain-specific-language, one with little or no resemblance to JavaScript as it normally exists.

Take what you will from John’s post and the enthralling discussion that followed. I for one am not too fussed either way; the syntax itself is seamless and somewhat more graceful than current techniques but, if adopted, it would fall much more hardly on the developer to ensure proper indentation, without which the readability of the code would suffer massively.

I was only really interested in one aspect of this new chaining technique; event handling. Throughout the Dev thread there were a number of similar suggestions for easier event handling in jQuery; here’s one example (proposed by Ariel Flesler):

$('div') 
    .when('click') 
        .addClass('active') 
        .text('Hey') 
    .done() 
    // Also possible with on() 
    .on('mouseout') 
        .removeClass('active') 
        .text('Ho') 
    .done();

As Ariel stated, this would not be a replacement for the conventional callback technique; it’s just a convenient shortcut for the primitive event handling I mentioned before (an event triggers on an element; something happens to that element). While unsuitable for many scenarios, the “ultra-chaining” technique does offer a refreshing simplicity.

My only issue is with the fact that code could quite quickly become a nightmare to maintain. I’d be uncomfortable using it because of its dependence on indentation. I much prefer having the code I write represent the logical outline of the actions performed through the code; if a piece of code is not executed until later (e.g. a callback) then it should be logically contained in such a way that it infurs when and if it will execute; unfortunately “ultra-chaining” does not provide this explicit way of containing functionality (in fact, it does the opposite).

After reading through the discussion surrounding ultra-chaining and other similar endeavors I thought I may as well have a crack at it. Like I said, the only part that really interested me at this point was the event handling. So, in my efforts to create a simple and readable alternative to callback functions I eventually came to the conclusion that method chaining (or “ultra chaining”) was not the way to go; like I said before, it’s not well contained and the readability of it relies too heavily on indentation.

Function’less callbacks

// Regular callback functions:
$(elem).hover(function(){
    $(this).addClass('over');
}, function(){
    $(this).removeClass('over');
});
 
// Callback objects:
$(elem).hover({addClass: 'over'}, {removeClass: 'over'});

I’m not just musing here; you can check out the source which makes the above notation (“callback objects”) possible.

Instead of passing a function you can pass a regular object with key-value pairs. Each property name of this object is executed as a jQuery method with arguments specified in the corresponding property value. So, in the above example, jQuery’s addClass method would be executed with ‘over’ as its only argument.

I know what some of you are thinking; abstracting away the functional side of event handling is dangerous and relinquishes necessary control. While I agree with you (mostly) it’s important that you note the following:

  • No control is relinquished; you can still pass regular functions as handlers and they will be executed just as before. If a function and a data object is passed then the data object will be treated as before.
  • Much of event handling involves simply changing something on the corresponding element (E.g. Hover over a table row and it changes colour). This extension acknowledges this common requirement.

Other examples:

Here are some examples of the ways in which you can use the extension:

// E.g. #1
$('a').bind('click.namespace', {
    addClass: 'clicked',
    animate: [{fontSize: '+=2px'}, 300]
});
 
// E.g. #2
$('div').click({
    css: {
        color: 'black',
        background: 'orange'
    },
    attr: {
        id: 'something'
    }
});
 
// E.g. #3
var over = {
    css: ['color','red'],
    unbind: ['mouseover', over]
};
$('h1').mouseover(over);

Note: jQuery’s bind method accepts three parameters by default: an event type, a data object and a callback function. This extension will only activate when you pass an object without a function as the third parameter (as shown in #1 above). If you pass all three parameters then the second (object) will be treated as a regular data object.

I hope this has provided you with some perspective on ultra-chaining and its alternatives. Please feel free to make use of the extension (download), I’ve been using it without a problem so far…