Reputation: 343
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
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
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);
});
Upvotes: 0
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
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
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