views:

53

answers:

3

The following jQuery Javascript code is included on an otherwise empty page.

var element;
$(function() {
  for (var i = 0; i < 10; i++) {
    element = $('<div>' + i + '</div>');
    element.click(function() {
      alert(i);
    });
    $('body').append(element);
  }
});

The desired behavior is that this code should generate 10 div elements numbered from 0 to 9. When you click on a div element, an alert popup will show the number of the div element you clicked on (i.e. if a user clicks on the div element labeled '4', the alert popup should show the number 4).

The alert popup instead shows the number 10 regardless of which div element is clicked on.

How can I modify this code to make it behave in the desired way?

+2  A: 

You need to pass the variable as a function parameter.

For example:

function buildElement(i) {
    var element = $('<div>' + i + '</div>');
    element.click(function() {
        alert(i);
    });
    $('body').append(element);
}

$(function() {
    for (var i = 0; i < 10; i++) {
        buildElement(i);
    }
});

You can also do this with an inline anonymous function, like this:

$(function() {
    for (var i = 0; i < 10; i++) {
        (function(i) {
            var element = $('<div>' + i + '</div>');
            element.click(function() {
                alert(i);
            });
            $('body').append(element);
        })(i);
    }
});
SLaks
+1 For properly using closures and hacking around it like the other answers. But that should be `var element = ...`
Justin Johnson
@Justin: Fixed.
SLaks
A: 

Usually I hide the number in an id and get it out in some structured way:

$(function() {
    for (var i = 0; i < 10; i++) {
        element = $('<div id="id-'+i+'">' + i + '</div>');
        element.click(function() {
            alert($(this).attr('id').split('-')[1]);
        });
        $('body').append(element);
    }
});
artlung
The real problem here is not using a closure. See SLaks' answer.
Justin Johnson
A: 

You could do 1 of two things. You could use the contents of the div to output is or you could grab the index in the array from a selector.

This is an example for contents:

element.click(function() {
  alert($(this).text());
});

Or for the index:

element.click(function() {
  alert($('div').index(this));
});

I believe both should work

MacAnthony
While this looks like it works, this ignores the real and simple issue of properly using closures.
Justin Johnson
The question wasn't on how to use closures. I don't see an issue with offering another solution that addresses the question asked.
MacAnthony
You're right, the question wasn't on how to use closure, but closures are the appropriate answer and use the language as it is designed. Unnecessarily accessing the DOM is just that: unnecessary.
Justin Johnson