Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Closures issue. Any tips?

I have an problem with the code below that's related to closures, that I need some help with.

As you can see I am creating several images in the for-loop that i'm assigning different id's (ie. the numbers from the array). So far, so good. When I click on the different images I want the function showId to be called with the images id's as argument, but the problem is that the number that's used as an argument to the function always becomes nr 8 (the last number in the array). How can I solve this?

Thanks in advance.

var imageArea = document.getElementById('imageArea');
var nrArray = [1,2,3,4,5,6,7,8];

for (var i = 0; i < nrArray.length; i++){
    var image = document.createElement('img');
    image.src = "images/theimage.png";
    image.id = nrArray[i];
    imgArea.appendChild(image);
    image.addEventListener ('click',function() {showId(image.id);},false);
}
like image 939
holyredbeard Avatar asked Mar 02 '26 18:03

holyredbeard


1 Answers

There are a bazillion answers to this exact same question here on stackoverflow, i'll hunt some down in a minute, but here are the key points:

  • Javascript has lexical scope (not dynamic scoping)
  • The smallest Javascript scope in javascript is a function
  • Block do not have thier own scope
  • Javascript uses variable hoisting, which means all variables in a scope are found before the scope executes and moved to the beginning of the scope. Which means that var image = ... isn't where you think it is. There is only one image variable, which (due to lexical scoping) is the variable you are accessing in your closure, which at the conclusion of the loop will obviously point at the last iterated item.
  • Functions are first-class objects, which means they are treated like variables and can be assigned and passed around as such
  • you can create a stable scope for a variable to live in by creating a self-executing anonymous function

So for example:

(function(localImage) {
    image.addEventListener ('click',function() {showId(localImage.id);},false);
})(image);

Also, as others have pointed out, event listener closures are executed in the context that they were bound. So conversely, without having worry about using a close to fix a scope, you could do:

image.addEventListener ('click',function() {showId(this.id);},false);

Edit

Some links to similar questions and some different perspectives on the answer:

  • Accessing loop iteration in a sub-function?
  • Closures in a for loop and lexical environment
  • Strange things in JavaScript “for”
  • Event handlers inside a Javascript loop - need a closure?

I could go on but a bazillion is a big number...

like image 98
J. Holmes Avatar answered Mar 05 '26 07:03

J. Holmes



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!