The more I use the Yahoo User Interface Library (YUI), the more I am impressed with it. I have been converting my JQuery imValidateForm plugin to a YUI widget for a project that I am working on. I ran into a scope issue when a button was clicked. I have dealt with Javascript scope issues in the past, but I did not know how to fix it using YUI. For those of you who are not familiar, the ‘this’ keyword within a function refers to the object itself.
function artist() { this.name = ‘Bob Marley’; } |
In the example above, the ‘this’ keyword refers to the artist object. Here’s a more complex example:
function cart(id) { this.selectedItem = id; var btnSubmit = document.getElementById(‘submit’); btnSubmit.onclick = function() { var selected = this.selectedItem; }; } |
Javascript does not know what ‘this.selectedItem’ is because the ‘this’ is in the scope of the btnSubmit object. To get around the scope issue, I can do the following:
function cart(id) { this.selectedItem = id; var self = this; var btnSubmit = document.getElementById(‘submit’); btnSubmit.onclick = function() { var selected = self.selectedItem; } } |
‘self’ is the cart object, not the submit button. Simple scope correction. But there are times when Javascript applications become so complex that scope correction is not this easy. In these situations, I have to do more than add self = this. I found a simple function that handles scope correction:
function cart(id) { this.selectedItem = id; var self = this; var btnSubmit = document.getElementById(‘submit’); btnSubmit.onclick = function() { bindCallBack(getAlbums, self, self.selectedItem); } this.getAlbums = function(id) { }; } function bindCallBack(fn, scope) { var args = []; for (var n = 1; n < arguments.length; n++) args.push(arguments[n]); return function () { return fn.apply(scope, args); }; } |
Without the bindCallBack function, the button would be able to ‘see’ the getAlbums method because it is out of scope. (Note: I can also use the bindCallBack function to pass parameters to the getAlbums).
The YUI way
So I didn’t want to deal with scope correction when creating my YUI widget, but as it turns out, I did not have to because YUI has automatic scope correction.
function cart() { this.selectedItem = null; var btnSubmit = YAHOO.utils.Dom.get(‘submit’); btnSubmit.on("click", this.getAlbums, this, true); this.getAlbums = function() { }; } |
By adding ‘this’ (the cart object) and ‘true’ to the click event, the execution scope of the button click is now the cart object. Easy. Try it yourself.
You can also write it as:
.on(“click”, this.getAlbums, null, this)
which I prefer. Also for regular JS scope-correction function.call and function.apply are very handy to know about.
https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Global_Objects/Function/Apply
https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Global_Objects/Function/Call
Pat Cavit’s last blog post..YUI Uploader Updated
Interesting. I wish I could also pass a value.
@admin:
Passing more argument along with the scope is a build-in feature in YUI3.x. You can emulate it on YUI2 using “apply”, but you have to do it manually, like your method “bindCallBack”.
Here is the YUI3 documentation:
http://developer.yahoo.com/yui/3/event/
Check the section: “Using on: Controlling Context and Arguments”.
Thanks. I’ve actually learned a lot about building widgets from visiting your site.
You can pass a value, using normal YUI:
.on(”click”, this.getAlbums, value, this)
You can pass values w/ .apply & .call as well.
.apply:
func_name.apply(scope, singleArg)
func_name.apply(scope, [multiple, args])
.call
func_name.call(scope, singleArg)
func_name.call(scope, multiple, args)
Using scope correction in JS is pretty simple & super-useful.