2

I have the following JS:

for ( var i = 1; i <= 2; i++ ) {
    $(window).load(function() {
        alert(i);
    });
}

When the page loads, it gives me an alert twice as expected. But the strange thing is the value of i is 3 on both alert. I expect the value of i is 1 on the first alert and 2 on the second alert. What causes the problem? Thank you in advance!

UPDATE #1

What if I need to place the function inside the loop because I want to use number increment as selector? Is there a solution to this problem? Here is what I mean

for ( var i = 1; i <= 2; i++ ) {
    $( '.element-' + i + ' .span' ).click( function( event ) {              
        $( '.element-' + i + ' ul' ).show( 'fast' );         
    });
}

The click function is not fired because we already know that i = 3. I want the click function get fired when .element-1 .span and .element-2 .span is clicked. Is there a workaround?

maurisrx
  • 319
  • 4
  • 9

2 Answers2

5

I assume that what you actually want to do is have the for loop inside the $(window).load function like so:

$(window).load(function() {
    for (var i = 0; i <= 2; i++) {
        alert(i);
    }
});

This will run the for loop after the window is loaded.


Explanation for why you are getting 3 in your alerts

The reason you are getting 3 in your alerts currently is illustrated by this diagram:

TIME
|     The for loop is begun (a = 0)
|     The load event on the window is handled by the specified function the load
|         function itself does not run
|     i is incremented (i++) now i = 1
|     The load event on the window is handled again
|     i is incremented (i++) now i = 2
|     The load event on the window is handled again
|     i is incremented (i++) now i = 3
|     The condition of the for loop fails so execution is discontinued
|
|     The window is loaded
|     Each of the callbacks is run and at this point i = 3 so 3 is alerted each
|         time
Sumner Evans
  • 8,951
  • 5
  • 30
  • 47
2

You are creating a closure with captured variable i. When window.load event handlers execute, the loop has had completed and the value of this variable is 3.

for ( var i = 1; i <= 2; i++ ) {
    $(window).load((function(iter) {
        return function() { alert(iter); };
    })(i));
}

Update:

In your code, you are directly assigning an anonymous function to be an event handler for $(window).load. This function binds loop iterator variable i - as a variable, and, when this function executes, the value of i is what was assigned by all previously executed code, which is the completed loop - terminated when i became 3.

My code can be rewritten:

function getEventHandler(iter) {
  return function() { alert(iter); };
}

for ( var i = 1; i <= 2; i++ ) {
    $(window).load(getEventHandler(i));
}

getEventHandler returns a function (which becomes $(window).load event handler). That returned function binds iter parameter with the value this parameter has when getEventHandler executes - every time it executes. In the loop we call getEventHandler immediately in each loop iteration with the current (changing) values of i.

Igor
  • 15,833
  • 1
  • 27
  • 32
  • Thank you. Your code makes the alerts show the expected results. Then can I use the number increment as selector? Please see updated question. – maurisrx Feb 27 '15 at 04:13
  • As you saw, parameter `iter` inside the handler has expected value, when you return the handler by calling a function with loop variable parameter . You can, of course, use `iter` to construct jquery selector. – Igor Feb 27 '15 at 04:20
  • Thank you. Can you give me brief explanation how to write code like yours? I'm curious how it's working. When `$(window)` is loaded, a function is called with `iter` as parameter. The function is called along with `i` value which represents number increment. Is that correct? Where does the value of `iter` come from? – maurisrx Feb 27 '15 at 04:30
  • Thank you Igor! Your explanation makes things clear. – maurisrx Feb 27 '15 at 06:11