2
var rows = document.getElementsByClassName('row');
for (var i = 0, l = rows.length; i < l; i++) {
    if (i % 2 === 0) {
        $(rows[i]).click(function () {
            alert('I am line number ' + i);
        }
    }
}

Hi, how would I get actual line number for each row ? since all I get when I trigger click event on an even row, upper bound value is alerted (i.e: rows.length = 7, i would be 6 for each row clicked).

user1781040
  • 291
  • 2
  • 5
  • 12
  • Does this answer your question? [JavaScript closure inside loops – simple practical example](https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – Donald Duck May 15 '23 at 15:25

2 Answers2

8

The problem is that upon click event is triggered, the i variable was already changed by the loop iteration. Theoretically you can use closure to make things working, i.e.

for (var i = 0, l = rows.length; i < l; i++) {
    if (i % 2 === 0) {
        (function(i) {
            $(rows[i]).click(function() {
                alert("I am line number " + i);
            });
        )(i);
    }
}

Practically, if you use jQuery (as I understood from the code), it is easier to use :even selector:

$(".row:even").click(function() {
    alert("I am line number " + $(".row").index(this));
});
VisioN
  • 143,310
  • 32
  • 282
  • 281
  • AH you beat me to it! Good answer. – Francois Joly May 15 '13 at 13:00
  • NB: `.index()` is `O(n)`, not `O(1)`. Also, it'll only work as expected if all the elements are children of the same parent. – Alnitak May 15 '13 at 13:01
  • @Alnitak Yup. This is just example of how to get index of it. If there is a speed constraint, pure JS with one `for` loop will do the job perfectly. **UPD:** now the index will be calculated fine. – VisioN May 15 '13 at 13:04
  • FYI, the first code example here is missing a closing curly bracket to close "(function(i) {" – mattsoave Jul 23 '15 at 18:05
2

The reason you're getting the wrong number is that the event handler functions you're creating get an enduring reference to the i variable, not a copy of it as of when they're created.

The way to solve it is to have the handler close over something that won't change. Here are three ways to do that, the first is specific to jQuery (it looks like you're using jQuery):

jQuery's each

It looks like you're using jQuery, in which case you can use its each to get an index to use that won't change:

var rows = $(".row");
rows.each(function(index) {
    if (index % 2 === 0) {
        $(this).click(function() {
            alert('I am line number ' + index);
        });
    }
});

Now, the event handler function closes over the index argument of the call to the function we give each, and since that index argument never changes, you see the right number in the alert.

Use a builder function

(Non-jQuery) You can solve this with a builder function:

var rows = document.getElementsByClassName('row');
for (var i = 0, l = rows.length; i < l; i++) {
    if (i % 2 === 0) {
        $(rows[i]).click(buildHandler(i));
    }
}
function buildHandler(index) {
    return function () {
        alert('I am line number ' + index);
    };
}

Here, the event handler function closes over the index argument in buildHandler, and since that index argument never changes, you see the right number in the alert.

forEach

(Non-jQuery) You can also use ES5's forEach function (which is one of the ES5 features you can shim on a pre-ES5 environment) to solve this:

var rows = document.getElementsByClassName('row');
Array.prototype.forEach.call(rows, function(row, index) {
    if (index % 2 === 0) {
        $(row).click(function () {
            alert('I am line number ' + index);
        });
    }
});

This works the same way as the two above, by closing over index, which doesn't change.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 1
    @Alnitak: Not necessarily. That could be any of several other minor libraries that use `$` and provide a `click`. I'll be it **is** jQuery, but that doesn't mean it is. (And the other options will be useful for people finding this if it happens *they* don't use jQuery. The fools.) – T.J. Crowder May 15 '13 at 13:06
  • 2
    @Alnitak: All three of them are closure-based answers. – T.J. Crowder May 15 '13 at 13:08