I searched the web for quite a while and couldn’t find a good answer to this problem, so I figured I’d post it to save the next person a little time.
I have a situation where I want to send an event to an instance of a JavaScript object. I could get it to go to the object itself, but was having trouble getting it to apply to only a specific instance of the object.
The answer was surprisingly simple – just wrap the pointer to your event handler function in an anonymous function. Doing this gives the anonymous function (closure) access to the variables currently in scope, which means that I have access to my object instance now.
The code is below. The commented out event listener add lines were the original attempt that would result in “undefined” being printed to the screen instead of my instance variable value. The uncommented version correctly calls my instance, which prints “test” to the debug area of the screen each time it is clicked.
// The object to handle the event... function TestListenerObj(txt) { this.eventText = txt; this.handleEvent = function() { var debug = document.getElementById("debug"); debug.innerHTML = debug.innerHTML + this.eventText + "<br/>"; return false; } } // Sets up the event handler function init() { var link = document.getElementById("test_link"); var obj = new TestListenerObj("test"); if (link.addEventListener) { // link.addEventListener("click", obj.handleEvent, false); // Calls "class" level handleEvent() link.addEventListener("click", function() { obj.handleEvent(); }, false); // Calls instance level handleEvent() } else { // link.attachEvent("onclick", obj.handleEvent ); // Calls "class" level handleEvent() link.attachEvent("onclick", function() { obj.handleEvent(); }); // Calls instance level handleEvent() } }
For a working example go here. For complete source visit this link and view source. Just click the “Test” link on the example to see the event be handled by the object, which in this case just prints to the debug area.
I plugged this in and uploaded the new version of the file. It is here:
http://www.mcdonaldland.info/files/ObjectEventHandler/ObjectEventHandler2.html
Thanks for the tip, Jeremy.
Thanks, Jeremy.
I’ve only rarely needed to use objects in JavaScript since most of what I use it for is page based functionality and I can honestly say that I have probably never used the self reference in JavaScript before. I’ve got a piece of functionality that I want to wrap up now (post coming soon) and decided to make it a true object instead of a bunch of copy/paste JavaScript code.
I found a couple different ways of handling the browser event binding problem other than these two but they were all pretty convoluted and had the hack smell to them.
Good post – function binding is one of the more non-intuitive aspects in JavaScript. FWIW, you could alternatively achieve the same thing using the closure already present in your handleEvent function. For example…
function TestListenerObj(txt) {
var self = this; //use this in our closure
this.eventText = txt;
this.handleEvent = function() {
var debug = document.getElementById("debug");
debug.innerHTML = debug.innerHTML + self.eventText + "";
return false;
}
}
By referencing “self” in the handleEvent function, you can use your commented-out version of the event bindings, which is nice because you avoid the additional closures. The root of the problem is that the browser will automatically bind the event handler to the element that triggered the event (i.e., #test_link). Your closure version subverts that behavior by causing the binding to only apply to the anonymous function, not to obj.handleEvent itself. This is a long-standing pain in the butt, which finally has a proper solution in ES5 via Function.prototype.bind.