Non-Dom Element Event Binding with jQuery
- by Rick Strahl
Yesterday I had a short discussion with Dave Reed on Twitter regarding setting up fake ‘events’ on objects that are hookable. jQuery makes it real easy to bind events on DOM elements and with a little bit of extra work (that I didn’t know about) you can also set up binding to non-DOM element ‘event’ bindings. Assume for a second that you have a simple JavaScript object like this: var item = { sku: "wwhelp" ,
foo: function() { alert('orginal foo function'); }
};
and you want to be notified when the foo function is called.
You can use jQuery to bind the handler like this:
$(item).bind("foo", function () { alert('foo Hook called'); } );
Binding alone won’t actually cause the handler to be triggered so when you call:
item.foo();
you only get the ‘original’ message. In order to fire both the original handler and the bound event hook you have to use the .trigger() function:
$(item).trigger("foo");
Now if you do the following complete sequence:
var item = { sku: "wwhelp" ,
foo: function() { alert('orginal foo function'); }
};
$(item).bind("foo", function () { alert('foo hook called'); } );
$(item).trigger("foo");
You’ll see the ‘hook’ message first followed by the ‘original’ message fired in succession. In other words, using this mechanism you can hook standard object functions and chain events to them in a way similar to the way you can do with DOM elements. The main difference is that the ‘event’ has to be explicitly triggered in order for this to happen rather than just calling the method directly.
.trigger() relies on some internal logic that checks for event bindings on the object (attached via an expando property) which .trigger() searches for in its bound event list. Once the ‘event’ is found it’s called prior to execution of the original function.
This is pretty useful as it allows you to create standard JavaScript objects that can act as event handlers and are effectively hookable without having to explicitly override event definitions with JavaScript function handlers. You get all the benefits of jQuery’s event methods including the ability to hook up multiple events to the same handler function and the ability to uniquely identify each specific event instance with post fix string names (ie. .bind("MyEvent.MyName") and .unbind("MyEvent.MyName") to bind MyEvent).
Watch out for an .unbind() Bug
Note that there appears to be a bug with .unbind() in jQuery that doesn’t reliably unbind an event and results in a elem.removeEventListener is not a function error. The following code demonstrates:
var item = { sku: "wwhelp",
foo: function () { alert('orginal foo function'); }
};
$(item).bind("foo.first", function () { alert('foo hook called'); });
$(item).bind("foo.second", function () { alert('foo hook2 called'); });
$(item).trigger("foo");
setTimeout(function () {
$(item).unbind("foo");
// $(item).unbind("foo.first");
// $(item).unbind("foo.second");
$(item).trigger("foo");
}, 3000);
The setTimeout call delays the unbinding and is supposed to remove the event binding on the foo function. It fails both with the foo only value (both if assigned only as “foo” or “foo.first/second” as well as when removing both of the postfixed event handlers explicitly. Oddly the following that removes only one of the two handlers works:
setTimeout(function () {
//$(item).unbind("foo");
$(item).unbind("foo.first");
// $(item).unbind("foo.second");
$(item).trigger("foo");
}, 3000);
this actually works which is weird as the code in unbind tries to unbind using a DOM method that doesn’t exist. <shrug>
A partial workaround for unbinding all ‘foo’ events is the following:
setTimeout(function () {
$.event.special.foo = { teardown: function () { alert('teardown'); return true; } };
$(item).unbind("foo");
$(item).trigger("foo");
}, 3000);
which is a bit cryptic to say the least but it seems to work more reliably.
I can’t take credit for any of this – thanks to Dave Reed and Damien Edwards who pointed out some of these behaviors. I didn’t find any good descriptions of the process so thought it’d be good to write it down here. Hope some of you find this helpful.© Rick Strahl, West Wind Technologies, 2005-2010Posted in jQuery