Dealing with Javascript variable scope and for-loop or external function.

26 août 2008 at 12 h 44 min 7 commentaires

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:

www.jibbering.com/faq/faq_notes/closures.html

Entry filed under: english publications, Javascript. Tags: , , .

The fastest way to log4j … Probleme de portée de variable et boucles for avec javascript

7 commentaires Add your own

  • 1. Shankara Shivagana  |  3 septembre 2008 à 21 h 12 min

    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.

    Répondre
  • 2. haveacafe  |  11 septembre 2008 à 13 h 25 min

    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…

    Répondre
  • 3. pyromosh  |  28 septembre 2008 à 1 h 20 min

    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!

    Répondre
  • 4. haveacafe  |  28 septembre 2008 à 19 h 48 min

    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…

    Répondre
  • 5. Anthony Alexander  |  27 mai 2009 à 3 h 09 min

    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.

    Répondre
  • 6. Anthony Alexander  |  27 mai 2009 à 3 h 22 min

    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.

    Répondre
    • 7. haveacafe  |  3 juin 2009 à 18 h 12 min

      Nice trick and delightfull framework @ novatvmedia !
      Thanks for your participation !

      Répondre

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

Trackback this post  |  Subscribe to the comments via RSS Feed


Articles récents


%d blogueurs aiment cette page :