RSS - Latest posts Hitch. Object-oriented event handlers with jQuery

Home » 2009 » 06 » Hitch. Object-oriented event handlers with jQuery

Moving to jQuery from YUI has been a 99% positive experience. Probably the only thing I’ve really missed was the ability provided by YUI Event to control the object scope that an event handler was executed in.

When you attach an event to an element in jQuery within the event handler function the this keyword always refers to the element that triggered the event.

$('a').bind('click', function() {
    alert(this);
});

Here this will be the a element that triggered the event. For the majority of the time this behaviour is absolutely ideal. However, if you’re using object oriented javascript and are trying to handle an event using a method of one of your classes, the chances are you’d want this to refer to the instance of your class instead of the DOM element.

var Klass = function() {
    this.message = 'hello';
};
Klass.prototype.handleClick = function(e) {
    alert(this.message);
};
var obj = new Klass();

$('a').bind('click', obj.handleClick);

The above fails because when Klass.prototype.handleClick is triggered from the click event this still refers to the a element that was clicked on. Hence this.message is undefined.

To solve this problem I made a small jQuery plugin called hitch that allows you to control the object scope. The above example would be rewritten as

var Klass = function() {
    this.message = 'hello';
};
Klass.prototype.handleClick = function(e) {
    alert(this.message);
};
var obj = new Klass();

$('a').hitch('click', obj.handleClick, obj);

If the handleClick method needs to access the DOM element that triggered the event, that can still be accessed through e.target. If the object scope argument is omitted then hitch should work in the same way as the standard bind method.

The plugin itself is just a few lines

(function($) {
    $.fn.hitch = function(ev, fn, scope) {
        return this.bind(ev, function() {
            return fn.apply(scope || this, Array.prototype.slice.call(arguments));
        });
    };
})(jQuery);

I believe similar functionality is scheduled to be included in an upcoming jQuery release, but for the time being I’m finding this plugin to be exceptionally useful.

 

You can leave a response, or trackback from your own site.

Add Comment

  1. agit8 says:

    Just what I was looking for!

    Super simple ; yet very useful.

    Hopefully this will be default behaviour of bind in the future.

    -b

  2. mslade says:

    This is very useful. As an alternative, you can achieve this using jQuery built-in “bind”, which allows you to specify the event (“click”), extra data (obj), and the function to handle the event (obj.handleClick). Your “extra data” is arbitrary and will be available inside your handler as e.data, so you can alert(e.data.message);

    I didn’t test this against your specific example, but it should look something like this:

    $(‘a’).bind(‘click’, obj, obj.handleClick);

    See http://docs.jquery.com/Events/bind.

  3. dan says:

    this also works..

    … assume the methods below are contained within a class

    this.doWork = function()
    {
    var me = this;
    $(‘#btnFoo’).bind(‘click’, function() {
    me.doOtherWork();
    });
    };

    this.doOtherWork = function()
    {
    alert(‘worky’);
    };

  4. David Schreiber says:

    Suuuuuuper ! :-) That’s the tool I need at the moment! Wohoo :-)

  5. [...] Alternatively, you can use a plug-in function that allows you to explicitly specify the scope of the handler. Here is an example (originally from ThinkRobot: [...]

  6. Ravi Pathak says:

    This is really cool. Does anyone know whether JQuery 1.6 has similar feature?

  7. Gabriel says:

    In newer jQuery versions, you can use the “proxy” function which sets the context of “this” to an object of your choosing. See http://api.jquery.com/jQuery.proxy/