I just started the fun upgrade process from jQuery 1.6 to 1.7 across our apps. One of the things that’s been missing from jQuery for a long time are non-DOM based events. In places where I needed them, I took the bind/unbind/trigger methods from Backbone.js.

Now $.Callbacks enters the room with a standing ovation. It lets developers control lists of callbacks that will all fire at once. For example you can create

    // Register list
    var cartUpdated= $.callbacks();

    // Add callback1 to list
    cartUpdated.add( callback1 );

    // Add callback2 to list
    cartUpdated.add( callback2 );

    // Fire so both callbacks run
    cartUpdated.fire( "quantity", "price" );

What I find is that this syntax is very powerful in most basic cases with one file. It becomes too coupled once more than one file is introduced though. To alleviate the coupling problem, I have created a small singleton, Events. The full singleton code is a gist here. The idea is that Events wraps fire/add/remove with trigger/bind/unbind to get the power of traditional DOM-related events. For instance I can trigger any event on an element at any point, without worrying if anything was bound or created beforehand. $.Callbacks doesn’t natively allow that. As you saw above, you need a reference to cartUpdated. This singleton allows for that blind firing that proves useful in many cases.

Let’s say I have two separate parts of the site, with two separate files – but they both care about the cart being updated. This would be common in an MVC structure where there are two Views listening and a controller dispatching. My two Views don’t know about cartUpdated so what to do? In comes the Events object and the following code:

    // Inside View 1, bind callback1
    $('#cart_total').bind( 'cartUpdated', callback1 );

    // Inside View 2, bind callback2
    $('#cart_total').bind( 'cartUpdated', callback2 );

    // Inside controller, trigger the event.
    Events.trigger( 'cartUpdated', 5, 10.00 );

The advantage of the above code allows us to blindly fire events without any other files having to worry about it being registered first. To be clearer, I don’t need to worry about saving off cartUpdated somewhere from the first example in comparison to the second.

Anyway I made this in a few minutes after checking out callbacks so I’m very open to thoughts and feedback so let me know and I’ll get back.

UPDATE 11/23/2011

I have to thank @jaubourg for the back and forth on the gist.

He noted how you can’t update options after the fact so he took it out.

I went the other way and made it so you can update options but I took context out, since that can be done externally anyway. You’d just need to use $.proxy and save that off for your bind/unbind.

( func != $.proxy( func ) )

The gist now has the full code plus my little test using the new function, updateOptions. It’s also now using the revealing module pattern, rather than being a plain singleton, thanks to Julian’s suggestion. Enjoy.