How to keep some context attached to a JavaScript event handler?

The problem is the following... You want to attach a handler to a DOM event but you want some information to remain associated with it. Let's say for example that the handler is a method of an object that makes use of the "this" pointer somewhere in its body (as it should, otherwise it should probably be static). As the API to attach a handler just takes a function pointer, and the "this" pointer is determined by the DOM element that triggered the event, it seems difficult to do. The Microsoft Ajax Library (like almost all Ajax libraries, let's be honest) provides an easy way to work around that. If you call:

var myDelegate = Function.createDelegate(this, this.myHandler);

then myDelegate will contain a reference to the this.myHandler function within which "this" will always mean the same thing as when you called createDelegate. For those of you who want to know how this works, feel free to look at the code. It's actually a simple application of closures.

We also have a variant of this that associates arbitrary context (not just the meaning of "this") with a function pointer. You can think of it as a differed execution of a function where the parameters are determined in advance:

var myCallback = Function.createCallback(myHandlerFunction, {foo: "bar"});

The callback function that this command creates can be called without any arguments but it will remember the argument that was passed in when the callback was created. For example, if the function body is:

function myHandlerFunction(p) {
    alert(p.foo);
}

Then calling myCallback() will display an alert with "bar" as the message. Now if you want to use this as an event handler and hook it some HTML element, you have to know that the function will be called with the DOM event object as the first parameter and your context parameter will be demoted to second parameter:

function myHandlerFunction(e, p) { // DOM event handler here
    alert(e.target.tagName + " raised an event with parameter " + p.foo);
}

But if you're building a component and all you want to do is hook up a bunch of DOM events to instance methods where you use the "this" pointer to refer to your component instance, the easiest way is probably to use $addHandlers (don't forget to also call $clearHandlers from dispose...):

$addHandlers($get("myElement"), {
        click: this._onclick,
        focus: this._onfocus,
        blur: this._onblur
    }, this);

This will create the delegates to _onclick, _onfocus and _onblur for you and attach them to the click, focus and blur events of myElement. That's a great helper if you're developing controls or behaviors.

There are a few more complex things that you can do with createCallback and createDelegate but that should give you the essential information. Feel free to ask questions in the comments section if you want to know more.

UPDATE: I forgot to mention that those techniques, beyond being quite common in JavaScript/DHTML, are closely related to "currying": http://en.wikipedia.org/wiki/Currying

UPDATE 2: I made the DOM event case a little clearer and included an example of that.

8 Comments

  • Great article! This really helps.

    What I used to do before reading this post is:

    var self = this;
    CallWebServiceMethod(function(results) { self.OnComplete(results); });

  • Well, I built an AJAX Control, it is a simple Custom ComboBox. if acctually uses javascript extensions provided by ASP.NET AJAX Framwork to show and hide and select items from a simle list control "". this control is working fine with UpdatePanel as it rais post back and also . I tried to use it withing Telerik Ajax Panel, and with Telerik Ajax Manager. I'me not sure if you used them before. With Telerik Ajax Manager displays a Javascript error and with Ajax Panel it is not updating panel content as well as stops to response to javascript events e.g, click on text box to show the list box etc...
    Do you have any idea about that!?

  • Muhammad: you'll have to ask Telerik...

  • This is also usefull when calling webservices with a callback handler. By using a delegate the callback will be executed in the same context as the function calling the webservice, something that is extremely usefull when calling webservices from within custom objects.

  • Hey Bertrand,

    Hope all is well.

    I've used this technique before for web services as Sean mentioned above and for attaching events to external objects (i.e. the page request manager).

    Thx for confirming that this was the correct way to maintain context with the current object.

    What else can this technique do for us (the advanced topics you hinted at)? I'd love a follow up post on this topic.

    Thx, Joel

  • Is there any reason to not just use closures? This seems like a pretty uninteresting method. Closures do this, and a bunch more. Is there something I'm not seeing?

  • t3knomanser: yes, what you're not seeing is that this is implemented using closures. If you're confortable with using closures directly, that's fine but you need to be careful as you may inadvertently attach more context than you need and even create hard to find memory leaks in IE. The methods presented here are more convenient, they hide a concept that is difficult or unknown to many developers who are new to JavaScript, and they only capture in the closure what little context *you* decide is useful.

  • Thank you so much for the explanation, Bertrand!

    I am a little noob with JavaScript, but I finally understood how to properly pass parameters to event handlers and it WORKS!

    Life is wonderful again :)

    All the best!

Comments have been disabled for this content.