Dealing with Javascript variable scope and for-loop or external function.
août 26, 2008
Sometimes, you need to set some HTML attributes automatically with a batch declaration.
By example imagine you want to set an mouseover over hundred of <a> links, in order to display their title content in an alert box. How to proceed ?
You could try to achieve this using a loop. Something like:
<html>
<a id="0" href="./0" title="the first"> link number 0 </a>
<a id="1" href="./1" title="the second"> link number 1 </a>
<a id="2" href="./2" title="the third"> link number 2 </a>
<a id="3" href="./3" title="the fourth"> link number 3 </a>
<a id="4" href="./4" title="the fifth"> link number 4 </a>
</html>
<script>
var number_of_link = 5;
for(i=0;i<number_of_link;i++){
document.getElementById(i).onmouseover =
function(){
alert("I'm the number "+i+" link")
};
}
</script>
Just try this tiny snippet: have you seen the problem ?
Every link has a function showing the last value of the i variable!
So, what does happened ?
We start iterating over a integer called i. This variable is in a global socpe. For each element found, we set a new function, wich display the i value. But the variable is passed by raference, and we want it to be passed by value !
Fortunately, we have a solution: we will dynamically add a new Function object, which will result in “statically” defined function.
<html>
<a id="0" href="./0" title="the first"> link number 0 </a>
<a id="1" href="./1" title="the second"> link number 1 </a>
<a id="2" href="./2" title="the third"> link number 2 </a>
<a id="3" href="./3" title="the fourth"> link number 3 </a>
<a id="4" href="./4" title="the fifth"> link number 4 </a>
</html>
<script>
var number_of_link = 5;
for(i=0;i<number_of_link;i++){
document.getElementById(i).onmouseover =
new Function("alert("I'm the number "+i+" link)");
}
</script>
The Function constructor allow us to take a String, and declaring it as a function. This tip forces the js interpetor to build the string, and only after, to build the function itself. So we achieve to have a static number written in each function we defined and desired omouseover effect.
For further information about function creation, i should recommend you this page:
www.howtocreate.co.uk/tutorials/javascript/functions
Have a look too, to some article describing the javascript closure:
Entry Filed under: Javascript, english publications. Mots-clefs: ECMAScript, Javascript, new Function.
7 Comments Add your own
Leave a Comment
Some HTML allowed:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <pre> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>
Trackback this post | Subscribe to the comments via RSS Feed
1.
Shankara Shivagana | septembre 3, 2008 at 9:12
Nice trick.
The only problem I found was when setting the event handler within a member function to another member function. I suppose the key would be properly using the “this” pointer. I probably lack the proper understanding of JavaScript. Does anyone know how to do this?
The example:
…
SomeMemberFunc: function()
{
var i = “closures can be magical!”;
var self = this;
someElement.onclick = new Function(self +”.SomeOtherMemberFun(” + i + “)”);
}
I do have an alternative solution: set an attribute on the element that the event is being set for. Once the event handler is called, access the targetElement (srcElement) and call getAttribute to retrieve the data.
2.
haveacafe | septembre 11, 2008 at 1:25
I actually don’t figure out what you are exactly trying to do.
I suppose you want apply the SomeOtherMemberFun when clicking on someElement, right ?
Perhaps the call() and apply() javascript methods could help you
function.apply(thisValue, functionArguments[]) allows you to call a function and specify what the keyword “this” will refer to within the context of the function.
Let me know and give me some examples if you don’t find the right way to apply your function to your element…
3.
pyromosh | septembre 28, 2008 at 1:20
That doesn’t seem to work at all for me. The second code example does exactly what the first one does. It passed by reference rather than passing by value.
Instead, I came up with an alternate solution to the same problem:
var Alpha = document.getElementsByTagName(’A');
for(i=0;i<Alpha.length;i++){
document.getElementsByTagName(’A')[i].onmouseover =
new Function(”alert(’I am the number ‘+this.id+’ link.’)”);
}
Also, you’re using non-standard (extended character) quotes in your code example. Not a huge deal to change them to run your sample code, but it is a pain in the ass.
I hope this helps!
4.
haveacafe | septembre 28, 2008 at 7:48
As standard does matter, you should use lowercase “a” as tag name instead of the caps “A” tag.
It’s really confusing, because the closure works the same in our both example.
Check your code using document.getElementById, and it would be nice to post it here, i wish i could help you to debug it and learn perhaps something i could miss!
Perhaps document .getlementById() doesn’t work because of non standard tag name and xHTML strict doctype declaration…
5.
Anthony Alexander | mai 27, 2009 at 3:09
Apparently when ever i have a problem it seems unsolvable.
There are several techniques I use. I tend to stay away from eval or functions built for strings, for a simple expression its fine but for a ‘class’ its usually unacceptable.
Another trick i use is to declare an array in the closure scope:
var closure_helper=0;
for(i=0;i<number_of_link;i++){
document.getElementById(i).onmouseover = function(){
var index=closure_helper++;
code…
}
You may have noticed that it only works for functions that are expected to be executed sequentially, which in most cases isnt very useful.
i think I have a few others but I cant remember right now, dang, seems im going to have to run through my whole framework again looking for these inconsistencies.
6.
Anthony Alexander | mai 27, 2009 at 3:22
PS, I just figured it out. Loop closures. Quickly:
for(var i=0,j=n;i<j;i++)(function(i){
// the first closure that sucked
_slider.whilesliding("update"+i,function(s){
_msg("updating element "+i+", "+s);
});
}) (i);
found it here, a lot of help.
http://trephine.org/t/index.php?title=JavaScript_loop_closures
basically a normal for loop except the loop closure preservers the iterator by passing it as a parameter.
7.
haveacafe | juin 3, 2009 at 6:12
Nice trick and delightfull framework @ novatvmedia !
Thanks for your participation !