simekadam
simekadam

Reputation: 7384

Working with closures in JavaScript

I have this function

function createSlidingGallery(){
    gallery_position = parseInt(jQuery(".imageWrapper.default").attr("rel"));
    gallery_position_min = 0;
    gallery_position_max = parseInt(jQuery("#galleryEl .sig_thumb a").size() - 1);
    var galleryWrapper = document.createElement("div");
    galleryWrapper.className = "sGalleryWrapper";
    for (var i = 0; i < gallery_position_max; i++) {
        var slide = document.createElement("div");
        slide.className = "slide slide"+(i);
        slide.setAttribute('rel', i);
        galleryWrapper.appendChild(slide);
    };
    jQuery(".imageWrapper.top").append(galleryWrapper);


//HERE COMES THE PROBLEM PART

    for (var i = 0; i < gallery_position_max; i++) {
        var position = i;

//THE CALLBACK ACTUALLY USES THE SAME CONTEXT FOR ALL PARTICULAR CALLS SO THAT THE POSITION VALUE HOLDS THE MAXIMUM VALUE IN ALL INSTANCES

        loadImage(position, false, function(index){
            console.log(index);
            jQuery(".slide"+position).css({
                'background': 'url('+backgroundImages[index].src+')'
            });

        });
    };
    hideLoadingDC();


}

What it should do is asynchronously load images into dynamicaly created elements. It actually creates all the elements, and it loads images as well. But there is function called loadImage which is intended to preload images and then save the information that this images has been already loaded and are propably cached. I am calling it with callback function which handles the loaded image and sets it as a background to appropriate element.Hence I need to hold the information about the element (like pointer, or index/position). Now I am trying to propagate its index to the function, but because the callback function is called after some time the position variable has already other value (the for loop actually goes through and in all calls of the callback it is set to the maximum value)

I know I can alter the loadImage function and add the position as another attribute, but I would prefere any other solution. I do not want to alter the loadImage function.

Upvotes: 0

Views: 133

Answers (2)

omartell
omartell

Reputation: 91

The problem is that all the callback functions are referencing the same i value, and not actually tracking the value at the time of iteration. So, you need to create a new scope (a closure) that creates a new reference for each different value of i.

A new scope can be created in JS with a function. Your code needs to be wrapped with an anonymous function and execute that function for each different value of i.

for (var i = 0; i < gallery_position_max; i++) {
    var position = i;
    loadImage(position, false, (function(index){
      return function(){
        console.log(index);
        jQuery(".slide"+position).css({
            'background': 'url('+backgroundImages[index].src+')'
        });
      };})(i));
};

   

Upvotes: 2

sth
sth

Reputation: 229593

You can use a helper function to create a new scope for the position variable:

function makeGalleryCallback(position) {
   return function(index){
         console.log(index);
         jQuery(".slide"+position).css({
                'background': 'url('+backgroundImages[index].src+')'
             });
      };
}

function createSlidingGallery(){
    ...
    for (var i = 0; i < gallery_position_max; i++) {
       loadImage(i, false, makeGalleryCallback(i));
    }
    ...
}

Upvotes: 2

Related Questions