views:

24

answers:

3

In this example I create three buttons 'one' 'two' 'three'. When clicked I want them to alert their number:

<html>
    <head>
        <script type="application/javascript" src="jquery.js"></script>
        <script type="application/javascript">
            $(document).ready(function() {
                var numbers = ['one', 'two', 'three'];
                for (i in numbers) {
                    var nr = numbers[i];
                    var li = $('<li>' + nr + '</li>');
                    li.click(function() {
                        var newVariable = String(nr);
                        alert(i); // 2
                        alert(nr); // three
                        alert(newVariable); // three
                        alert(li.html()); // three
                    });
                    $('ul').append(li);
                }
            });
        </script>
    </head>
    <body>
        <ul>
        </ul>
    </body>
</html>

The problem is, when any of these are clicked, the last value of the loop's variables is used, i.e. alert box always says 'three'.

In JavaScript, variables inside for-loops seem to be 'static' in the C language sense. Is there some way to create separate variables for each click function, i.e. not using the same reference?

Thanks!

Edit:

The solution is to use jQuery.data to associate arbitrary data with each element:

<html>
    <head>
        <script type="application/javascript" src="jquery.js"></script>
        <script type="application/javascript">
            $(document).ready(function() {
                var numbers = ['one', 'two', 'three'];
                for (i in numbers) {
                    var nr = numbers[i];
                    var li = $('<li>' + nr + '</li>');
                    li.data('nr', nr);
                    li.click(function() {
                        alert($(this).data('nr'));
                    });
                    $('ul').append(li);
                }
            });
        </script>
    </head>
    <body>
        <ul>
        </ul>
    </body>
</html>
A: 

You need to attach any data on the elements or on http://api.jquery.com/jQuery.data/ in order to reuse later on.

e.g

...
var li = $('<li>' + nr + '</li>');
jQuery.data(li, "the-value", foo);
li.click(function() {
    var foo = jQuery.data(li, "the-value");
    ...
}
cherouvim
Yes, this solved the problem! The jQuery docs points out that you should use li.data instead http://api.jquery.com/data/. I've added the solution to the question. Thanks!
pthulin
A: 

This is working as it should, you are asking the click event to use these variables (not absolute values) and by the time the method is called the variables are at their 'max' values.

Try using the title attribute to store the value you want to return, i.e.:

var li = $('<li title="' + nr + '">' + nr + '</li>');

Then your alert would be:

alert(this.attr('title'));

Not tested but that should work or at least set you on the right track.

Lazarus
Thanks for the tip, but in my real use case I have a pretty large JSON structure that I want to associate with each button. I've edited the question to show how I solved it.
pthulin
+1  A: 

Alternately, place a closure inside the for loop.

        $(document).ready(function() {
            var numbers = ['one', 'two', 'three'];
            for (i in numbers) {
                (function () {
                var nr = numbers[i];
                var li = $('<li>' + nr + '</li>');

                li.click(function() {
                    var newVariable = String(nr);
                    alert(i); // 2
                    alert(nr); // three
                    alert(newVariable); // three
                    alert(li.html()); // three
                });

                $('ul').append(li);

                }());
            }
        });

It's also bad practise for using for in for iterating over an array. It should be strictly used for iterating over object fields. Use the for(;;;) instead.

Matt