Correct way to bind "this" in JavaScript event callbacks?

Triynko picture Triynko · Dec 2, 2015 · Viewed 16.5k times · Source

I created a class called SearchBox to handle search interaction (delayed trigger, search on enter key press, preventing searches while one is active, synchronizing results when a search completes and the text has changed, etc.).

All the class methods are prototype methods, meant to be accessed via this. In the following code, assume p is the class's prototype.

p.registerListeners = function () {
    $(this.element).on('keypress', this.searchKeyPressed);
};

p.unregisterListeners = function () {
    $(this.element).off('keypress', this.searchKeyPressed);
};

That doesn't work, because when the keypress event calls the searchKeyPressed handler, it doesn't do so in the context of this. The only solution I can come up with is one that only modern browsers support, which is to bind the callback to this, which actually creates a new function. Since it creates a new function, I have to cache it in order to remove it later, since I have to pass the same reference to off that I passed to on.

Is there a better way to do it than this, or is this ok?

var boundKeyPressed;

p.registerListeners = function () {
    boundKeyPressed = this.searchKeyPressed.bind(this);
    $(this.element).on('keypress', boundKeyPressed);
};

p.unregisterListeners = function () {
    $(this.element).off('keypress', boundKeyPressed);
};

I thought that maybe jQuery.on would provide a way to do this event binding automatically, but instead it seems like it binds this to different things depending on how it's called. For example, when using on('eventname',instance.func), this is the "currentTarget" (not necessarily "target" in bubbling terms), whereas when using on('eventname','selector',instance.func), this refers to the element matching the selector. In either case, the func runs as though it has no relationship with instance.

Answer

Koen. picture Koen. · Dec 2, 2015

If you add a namespace to your events you can bind events and easily unbind them all at once.

To bind:

$(this.element).on('keypress.mynamespace', this.searchKeyPressed.bind(this));

To unbind:

$(this.element).off('.mynamespace');