Run JavaScript Event-Handler in Another Context
1 Comments Published by John Eric Sobrepena on Sep 24, 2009
When a JavaScript function is called by an event such as "onclick" or "onmouseover", it runs in the context of the calling DOM element. What if you do not want this event-handler to run under the context of the DOM element but instead you would like to run it under a custom object that you have created? Let us have the following example.
Listing 1
So far so good. Now if you run this code and the click event of _divMain is triggered then you will get not the message "Hello World" but instead the message "undefined". What went wrong? Though OnClick is declared as a method of the object MessageHandle, the function will always run in the context of the calling object which is in this case _divMain and not the MessageHandle object. The "this" in line 6 is actually referring to _divMain. So how do we make sure that OnClick will run in the context of MessageHandle? We will create a new function and use the JavaScript's function apply().
Listing 2
Now here is our updated JavaScript code.
Listing 3
Listing 4
Listing 5
Listing 1
function MessageHandle(divObject, message) { this.Message = message; this.OnClick = function() { alert(this.Message); } divObject.attachEvent('onclick', this.OnClick); } var divObject = document.getElementById("_divMain"); var _h = new MessageHandle(divObject, "Hello World!");We are creating a function called MessageHandle that is later on used to instantiate a MessageHandle object at line 13. In the constructor of MessageHandle, we can see that the message ("Hellow World!") is stored in the property Message (line 3) and OnClick function is attached to the divObject's onclick event (line 9). The OnClick event handler simply shows an alert box showing the message "Hello World!".
So far so good. Now if you run this code and the click event of _divMain is triggered then you will get not the message "Hello World" but instead the message "undefined". What went wrong? Though OnClick is declared as a method of the object MessageHandle, the function will always run in the context of the calling object which is in this case _divMain and not the MessageHandle object. The "this" in line 6 is actually referring to _divMain. So how do we make sure that OnClick will run in the context of MessageHandle? We will create a new function and use the JavaScript's function apply().
Listing 2
function RunHandlerInContextOf(Context, Handler){ return function() { return Handler.apply(Context); } }The function RunHandlerInContextOf has two parameters Context and Handler. Handler is the event-handler to execute and Context is the object context that Handler will run under. Notice that RunHandlerInContextOf function returns a function that is a wrapper to the event-handler call at line 3.
Now here is our updated JavaScript code.
Listing 3
function RunHandlerInContextOf(Context, Handler){ return function() { return Handler.apply(Context); } } function MessageHandle(divObject, message) { this.Message = message; this.OnClick = function() { alert(this.Message); } divObject.attachEvent('onclick', RunHandlerInContextOf(this,this.OnClick)); } var divObject = document.getElementById("_divMain"); var _h = new MessageHandle(divObject, "Hello World!");Here is a JavaScript cross-browser utility I created for attaching event-handlers and gives you an option to run the event-handler in the default context or specify another object as the context.
Listing 4
var CoolScriptingUtil = { //cross-browser attach event. //Element - is the object where eventhandler will be attached to //EventString - is the event name to handle of Element //Handler is a function pointing to the event handler //Context is what context the Handler will be executed attachEvent: function(Element, EventString, Handler, Context) { if (typeof (Context) != 'undefined' && Context != null) { if (Element.addEventListener) // W3C DOM Element.addEventListener(EventString, CoolScriptingUtil.ContextOf(Context, Handler), false); else if (Element.attachEvent) // IE DOM Element.attachEvent('on' + EventString, CoolScriptingUtil.ContextOf(Context, Handler)); else alert('Unable to attach to event ' + EventString + ' of ' + Element.toString()); } else { if (Element.addEventListener) // W3C DOM Element.addEventListener(EventString, Handler, false); else if (Element.attachEvent) // IE DOM Element.attachEvent('on' + EventString, Handler); else alert('Unable to attach to event ' + EventString + ' of ' + Element.toString()); } }, //Returns a function that executes [Method] in the context of [ThisObj] ContextOf: function(ThisObj, Method) { return function() { Method.apply(ThisObj); }; } }To use this utility we can now replace the command on listing 3 line 15 "divObject.attachEvent('onclick', RunHandlerInContextOf(this,this.OnClick));" with the following.
Listing 5
CoolScriptingUtil.attachEvent(divObject, 'click', this.OnClick, this);The fourth parameter is optional. When you remove the fourth parameter, the event-handler will run in the default context.
Labels: apply context, cross-browser, event handler, JavaScript
thanks, turned out to be very useful to me