Tip - Using an Instance Method as a Callback or Event Handler

When doing object-oriented web programming, it's quite common to want to have a method on a specific instance called as an event handler, or as the callback for something like Ajax.Request. Usually, the first attempt looks like this:

var MyWidget = Class.create({
    initialize: function(name)
    {
        this.name = name;
        this.element = null;
    },
 
    showName: function()
    {
        alert('The name is ' + this.name);
    },
 
    hookElement: function(element)
    {
        this.element = $(element);
        if (!this.element)
        {
            throw "Element not found";
        }
        this.element.observe('click', this.showName); // <= WRONG
    }
});
function testWidget()
{
    var widget;
 
    widget = new MyWidget('Test Name');
    widget.hookElement($('testDiv'));
}

You code it up, call testWidget on page load, but when you click the div it says "The name is undefined" (or sometimes "This name is ___" but the name isn't the name you gave your widget). Why?

The problem (as you'll have guessed from the subtle comment) is with this line:

this.element.observe('click', this.showName); // <= WRONG

There's nothing about the showName function that binds it to the instance of the class you've created. In JavaScript, functions are just functions, they're not methods (even though sometimes we call them that). So when the function gets called, "this" does not refer to your widget instance, and it doesn't work.

To make the function work as intended, you must create a new function at runtime that binds the instance to the function you want to call, and use the new function as the callback/event handler. This sounds complicated, but it's actually very easy (even without Prototype, although there are some niggles). With Prototype, it's not easy, it's trivial: Just use the provided Function#bind function. Here's the new line of code:

this.element.observe('click', this.showName.bind(this));

Function.bind returns a function that, when called, will call the original function in a way that makes sure "this" is set to the value you gave #bind.

Some further reading:

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License