3

I have a jquery which works correctly when I do this:

var slide = [];
slide[1] = 
    {
        hide: function() {
            $("#slide-1").hide();
        },
        show: function() {
            $("#slide-1").show(2000);
        }
    };
slide[1].show(); <<< works fine

But if I try it in a loop in fails:

for (var i=1; i <= totalSlides; i++) {
  slide[i] = 
    {
        hide: function() {
            $("#slide-" + i).hide();
        },
        show: function() {
            $("#slide-" + i).show(2000);
        }
    };
};
slide[1].show();  << unassigned

any idea?

BenG
  • 1,756
  • 15
  • 17
  • 2
    Ӫ_._Ӫ's answer at the bottom nailed it I think, but here's a live example of more or less the same thing that's happening if that can be of help. http://jsfiddle.net/X4VQV/ – Richard Neil Ilagan Sep 30 '11 at 06:59
  • @richardneililagan: Very nice example to illustrate the issue. – user113716 Sep 30 '11 at 13:16
  • Make sense. Because I wrapped 'i' in closures, it's stored as a pointer instead of a value. When slide[1].show() is called later, i points to nothing, and throws error "undefined" – BenG Oct 05 '11 at 17:55

3 Answers3

3

Well, you're saying that it is "unassigned" but I'm guessing that the function is just not doing what you want.

This is a common issue. All the functions you're creating in the loop are referencing the same i variable. This means that when the function runs, it is getting the value of i where it was left after the loop finished.

You need to scope the variable that your functions reference in a new variable environment to retain the value from the loop. To do that, you need to invoke a function, and have that function reference the current i value.

Like this:

function generate_functions( j ) {
     //    v----- DO NOT place the opening brace on the next line, after the
    return {              // return statement, or your code will break!!!
        hide: function() {
            $("#slide-" + j).hide();
        },
        show: function() {
            $("#slide-" + j).show(2000);
        }
    };
}

var slide = [];

for (var i=1; i <= totalSlides; i++) {

    slide[i] = generate_functions( i );

};

slide[1].show(); // should work

I created a function called generate_functions(), and invoked it in each iteration, passing i as an argument.

You'll notice that generate_functions() received the value as the j parameter. You could call it i as well, but changing the name makes it a little clearer IMO.

So now your functions are referencing the local j. Because a new variable environment is created with each invocation of generate_functions(), the functions inside that you create will be referencing the j value of that specific variable environment.

So the generate_functions() returns the object that contains the functions that were created in each new variable environment, and that object is assigned to slide[i].

user113716
  • 318,772
  • 63
  • 451
  • 440
  • 1
    To expand on that a tiny bit, what you're creating is a __Javascript closure__. It can be a powerful language [feature] but it can be a big pain in the buttocks if you're not familiar with it (and thus, create it accidentally, like in your question). – Richard Neil Ilagan Sep 30 '11 at 06:52
0

Is the $("slide-1" + i).show(2000) a typo, or the error?

Dan
  • 1,002
  • 12
  • 24
0

Add var slide = []; above the for loop.

Chris Cherry
  • 28,118
  • 6
  • 68
  • 71