8

Please see the jsfiddle: http://jsfiddle.net/LsNCa/2/

function MyFunc() {

    for (var i = 0; i < 2; i++) { // i= 0, 1
        var myDiv = $('<div>');
        
        myDiv.click(function(e) {
            alert(i);    // both the two divs alert "2", not 0 and 1 as I expected
        });
        $('body').append(myDiv);
    }
}

var myFunc = new MyFunc();

I want the divs to alert "0" and "1" respectively when I click them, but both of them alert "2".

When I click the divs and the event is triggered, how and where do the handler find the value of the variable i?

I'm aware that adding a closure achieves my goal. But why?

1

4 Answers 4

7
    function MyFunc() {

    for (var i = 0; i < 2; i++) { // i= 0, 1
        (function(j) {
            var myDiv = $('<div>');

            myDiv.click(function(e) {
                alert(j);
            });
            $('body').append(myDiv);
        })(i);
    }
}

var myFunc = new MyFunc();

The code above is how you get it work correctly. Without an closure, you always the the last value of i. What we do is to post i into the closure and let the runtime "remember" the value of that very moment.

Sign up to request clarification or add additional context in comments.

3 Comments

Is this the standard way of doing it? If the loop body is fairly long, do we still wrap it inside an anonymous function?
This is the usual idiom. But you could use Zenwolf's style if you prefer for longer functions.
As I can see people usually write it this way. try not to do this in a big loop. I mean like for(var i = 0; i < 1000; i++).
2

You need a closure because all your event handler functions are referencing the same variable i. The for loop updates this, and when the loop is done the variable contains 2. Then when someone clicks on one of the DIVs, it accesses that variable.

To solve this, each event handler needs to be a closure with its own variable i that contains a snapshot of the value at the time the closure was created.

1 Comment

You said you know the correct way to write it, you just wanted an explanation of why that's needed. I'm not sure what code I can write that will illustrate the answer -- your code does that just fine.
1

I suggest that you read this article

JavaScript hoists declarations. This means that both var statements and function declarations will be moved to the top of their enclosing scope.

As @Barmar said in his answer above, the variable i is being referenced by both the event handlers.

You should avoid declaring functions inside loops. Below there is some code that does what you need.

I assume that you're using jQuery.

function MyFunc() {

    for (var i = 0; i < 2; i++) { // i= 0, 1
        var myDiv = $('<div>');

        $('body').append(myDiv);
    }
    $('div').on('click', function() {
        alert($(this).index());
    });
}

var myFunc = new MyFunc();

Comments

0

The "alert()" call happens after the for-loop completed, which means that the value of "i" will be the last value for anything after that. In order to capture individual values of "i", you must create a closure for each value by creating a new function:

function MyFunc() {

    function alertFn(val) {
        return function () {
            alert(val);
        };
    }

    for (var i = 0; i < 2; i++) {
        var myDiv = $('<div>');
        myDiv.click(alertFn(i));
        $('body').append(myDiv);
    }
}

var myFunc = new MyFunc();

The closure captures the value of "i" at the time it was passed into the function, allowing alert() to show the value you expect.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.