atmosphere506
atmosphere506

Reputation: 343

Javascript For-Loop

Currently I'm working on a project using RaphealJS library and everything seems to be alright until I encountered a problem like this.

Instead of doing this multiple times :

   dolphinIcon[1].click(function() {              
           this.attr({  
             stroke: 'black', 'stroke-width': 2,  
             fill: 'green'  
           }); 
           alert(1);
   });

   dolphinIcon[2].click(function() {              
           this.attr({  
             stroke: 'black', 'stroke-width': 2,  
             fill: 'green'  
           }); 
           alert(2);
   });

  dolphinIcon[3].click(function() {              
           this.attr({  
             stroke: 'black', 'stroke-width': 2,  
             fill: 'green'  
           }); 
           alert(3);
   });

Why can't I just do this ?

for(var i=0; i<dolphinIcon.length; i++){
     dolphinIcon[i].click(function() {              
        this.attr({  
           stroke: 'black', 'stroke-width': 2,  
           fill: 'green'  
        });      
        alert(i);
     });
}

I just want each icon which is stored in the array to alert() the number of its index, but when I use for-loop, it always alert() the same number(size of the array) no matter which icon I clicked on. How should I fix this ?

Upvotes: 1

Views: 496

Answers (5)

S. Mayol
S. Mayol

Reputation: 2625

Here I provide you a link of a code that I prepare to explain you with examples and details about: JavaScript for loop in three different ways, click the link read the code, test yourself and give a like.

https://code.sololearn.com/WHc3WmA7TrMP

Bellow is the code:

<!DOCTYPE html>
<html>
   <body>

      <script type="text/javascript">
         /*
        The For Loop. Bellow are three examples using the same code in different ways, 
        returning the same results. Before let's explain which are the components fo the for loop.

        for loop have 3 components:
        1.initialization 
        2.condition
        3.Iteration 

      syntax: for (Initialization;condition;iteration)

        e.g.: for (i=1; i<=5; i++)

        In JavaScript <br> this tag is used as a line break.
        */

        //The example below creates a for loop that prints numbers 1 through 5.
        document.write("The example below creates a for loop that prints numbers 1 through 5. <br/>");
        for (i=1; i<=5; i++) {
           document.write(i + "<br />"); // <br /> is use to line break
        }

        //Statement 1 is optional, and can be omitted, if your values are set before the loop starts.
        document.write("<br/> Statement 1 is optional, and can be omitted, if your values are set before the loop starts. <br/>");
        var i = 1;
        for (; i<=5; i++) {
           document.write(i + "<br />");
        }

        //Also, you can initiate more than one value in statement 1, using commas to separate them.
        document.write("<br/> Also, you can initiate more than one value in statement 1, using commas to separate them. <br/>");
        for (i=1, text=""; i<=5; i++) {
            text = i;
            document.write(text + "<br />");
        }

        /* 
        If you notice in the for loop in JavaScript is not mandatory to declare explicitly a variable.
        e.g.: for (i=1; i<=5; i++) {}

        this is equivalent to say:
        for (var i=1; i<=5; i++) {}

        */

        // the following code will generate an infinite loop if you do not include break;
        var i = 0;
        for (; ; ) {
            document.write(i);
            i++;
            // if you comment or delete the break, this for loop will never end
            break;
        }

      </script>

      <p>Please like this code, I hope it helps you to learn more about For Loop ...</p>
   </body>
</html>

Upvotes: 0

Jareish
Jareish

Reputation: 782

I'd like to suggest the underscore.js library. It contains many utility methods of dealing with arrays and onbject (in your case each and bind) http://underscorejs.org/#each

In your example this code would be reduced to:

_.each(dolphicons, function(dolphicon, index){  
    var func = function() {              
        this.attr({  
           stroke: 'black', 'stroke-width': 2,  
           fill: 'green'  
        });      
        console.log(index);
    }
    func = _.bind(func, dolphicon);

    dolphicon.click(func);
});

"this" will refer to the dolphicon because the bind. example alsoo at: http://jsfiddle.net/SyJdv/

You could alsoo scope the function outside the each loop

var func = function() {              
   this.obj.attr({  
      stroke: 'black', 'stroke-width': 2,  
      fill: 'green'  
    });      
    console.log(this.index);
}

_.each(dolphicons, function(dolphicon, index){  
   var clickfunc = _.bind(func, {obj: dolphicon, index: index});    
   dolphicon.click(clickfunc);
});

http://jsfiddle.net/PW9WX/1/

Upvotes: 0

webdeveloper
webdeveloper

Reputation: 17288

Try this:

for(var i=0; i<dolphinIcon.length; i++){ 
     dolphinIcon[i].bind('click', {index: i}, function(e) {             
        $(this).attr({   
           stroke: 'black', 'stroke-width': 2,   
           fill: 'green'   
        });       
        alert(e.data.index); 
     }); 
}

Upvotes: 2

Kevin Nielsen
Kevin Nielsen

Reputation: 4433

This is because of the way javascript closure works -- basically, your callback/event handling functions are binding to the loop variable i rather than to specific values of i in successive iterations of the loop.

Here's the simple workaround: simply wrap the interior of your loop with an anonymous function, and pass the loop variable to that function. This will cause the closure to attach to that specific value.

For example:

for(var i=0; i<dolphinIcon.length; i++)
{
    (   function(i) 
        {
            dolphinIcon[i].click(function() 
            {              
                this.attr({ stroke: 'black', 'stroke-width': 2, fill: 'green'});      
                alert(i);
            } );
        } )( i );
}

Upvotes: 3

gen_Eric
gen_Eric

Reputation: 227270

This is a classic JavaScript problem. The variable i in each callback function is the same one, which will be dolphinIcon.length once the loop is done.

You need to use a closure to "capture" the i variable.

var clickFunc = function(i){
    return function(){
        this.attr({  
           stroke: 'black', 'stroke-width': 2,  
           fill: 'green'  
        });      
        alert(i);
    }
};
for(var i=0; i<dolphinIcon.length; i++){
     dolphinIcon[i].click(clickFunc(i));
}

clickFunc will return a function that "closes" on the value of i.

You can also pass extra data to the click handler, to be used once it's called.

for(var i=0; i<dolphinIcon.length; i++){
     dolphinIcon[i].click({i: i}, function(e) {              
        this.attr({  
           stroke: 'black', 'stroke-width': 2,  
           fill: 'green'  
        });      
        alert(e.data.i);
     });
}

Upvotes: 5

Related Questions