Hello and welcome to our community! Is this your first visit?
Register
Enjoy an ad free experience by logging in. Not a member yet? Register.
Results 1 to 6 of 6
  1. #1
    New to the CF scene
    Join Date
    Nov 2012
    Posts
    8
    Thanks
    4
    Thanked 0 Times in 0 Posts

    for loop problems

    Code:
    <!DOCTYPE html>
    <html>
    <head>
    </head>
    <body>
    <button>Button</button> <button>Button</button> <button>Button</button> 
    <script>
    var button=document.getElementsByTagName("button"),i
    for(i=0;i<button.length;i++)
    {
    button[i].onclick=function()
      {alert(i)}
    alert(i)
    }
    </script>
    </body>
    </html>
    When the body loads, it gives 3 alerts with 0, 1, and 2. This is what I want to happen when I click the buttons.

    i.e. the first button alerts 0; the second 1; the last 2;

    But when I click the buttons, they all alert 3. I don't know why and can someone help?

  • #2
    Supreme Master coder! Old Pedant's Avatar
    Join Date
    Feb 2009
    Posts
    25,028
    Thanks
    75
    Thanked 4,324 Times in 4,290 Posts
    Time to learn about CLOSURES.

    Or time to do this in a completely different way.

    The simplest thing is to do it in a different way:
    Code:
    <!DOCTYPE html>
    <html>
    <head>
    </head>
    <body>
    <button>Button</button> <button>Button</button> <button>Button</button> 
    <script>
    var button=document.getElementsByTagName("button"),i
    for(i=0;i<button.length;i++)
    {
       button[i].value = i;
       button[i].onclick=function() { alert(this.value); }
    }
    </script>
    </body>
    </html>
    To use a closure, you could do something like this:
    Code:
    <!DOCTYPE html>
    <html>
    <head>
    </head>
    <body>
    <button>Button</button> <button>Button</button> <button>Button</button> 
    <script>
    var button=document.getElementsByTagName("button"),i
    for(i=0;i<button.length;i++)
    {
       button[i].onclick = buttonClick(i);
    }
    
    function buttonClick(n)
    {
        var nv = n;
        return function() { alert(nv); }
    }
    </script>
    </body>
    </html>
    I'll let you google for closure if you are interested in learning why that works.
    Last edited by Old Pedant; 10-21-2013 at 09:34 PM.
    An optimist sees the glass as half full.
    A pessimist sees the glass as half empty.
    A realist drinks it no matter how much there is.

  • Users who have thanked Old Pedant for this post:

    Md8^h2nx (10-21-2013)

  • #3
    Senior Coder
    Join Date
    Dec 2010
    Posts
    2,391
    Thanks
    11
    Thanked 568 Times in 561 Posts
    This is the infamous "closure in a loop" problem.

    Short description: A closure (which essentially is a function inside a function) has access to its surrounding scope, even if the code in this scope has finished executing. In your case, the inner function for the onclick event handlers has access to the variable "i" of the surrounding function scope (the global scope).

    But there is a booby trap: The closure has access to the scope at execution time and not at creation time. At execution time of the closure the outer function (the for loop) has already finished, so "i" will always be 3 in your case.

    Solution: Create another "inner closure" that returns a function which will be assigned to the onclick handler. This closure can be created using a self-executing anonymous function to which you hand over the loop variable as a parameter to create a local copy
    Code:
    for(i=0;i<button.length;i++) {
       button[i].onclick=(function(_innerI) {
          return function() {alert(_innerI);}
       })(i);
       alert(i);
    }
    Last edited by devnull69; 10-21-2013 at 09:01 PM.

  • #4
    New to the CF scene
    Join Date
    Nov 2012
    Posts
    8
    Thanks
    4
    Thanked 0 Times in 0 Posts
    Thank you very much for not only helping me, but teaching me something I didn't know.

    Edit:thanks to the both of you

  • #5
    Supreme Master coder! Old Pedant's Avatar
    Join Date
    Feb 2009
    Posts
    25,028
    Thanks
    75
    Thanked 4,324 Times in 4,290 Posts
    If it's not obvious, DevNull's code is just a "shorthand" way of doing the same thing I did.

    I find it easier to understand (and teach) closures by creating a separate out-of-line function. But there's certainly no reason you have to do that. DevNull's solution is spot-on.
    An optimist sees the glass as half full.
    A pessimist sees the glass as half empty.
    A realist drinks it no matter how much there is.

  • #6
    Senior Coder rnd me's Avatar
    Join Date
    Jun 2007
    Location
    Urbana
    Posts
    4,276
    Thanks
    10
    Thanked 581 Times in 562 Posts
    you can also use functional programming to side-step the scope issue, often resulting in cleaner code than an inline anon function:

    Code:
    <script>
    [].map.call(
       document.getElementsByTagName("button"), 
       function(button, i){
         button.onclick=function(){alert(i);};
    });
    </script>

    of the notion of functional approaches usually looking cleaner, i find this instance to be no exception.
    Last edited by rnd me; 10-21-2013 at 11:39 PM.
    my site (updated 13/9/26)
    BROWSER STATS [% share] (2014/5/28) IE7:0.1, IE8:5.3, IE11:8.4, IE9:3.2, IE10:3.2, FF:18.2, CH:46, SF:7.9, NON-MOUSE:32%

  • Users who have thanked rnd me for this post:

    devnull69 (10-22-2013)


  •  

    Posting Permissions

    • You may not post new threads
    • You may not post replies
    • You may not post attachments
    • You may not edit your posts
    •