Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Number value wrong in event bound through a for loop

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).

like image 445
user1781040 Avatar asked Feb 11 '26 12:02

user1781040


2 Answers

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));
});
like image 172
VisioN Avatar answered Feb 13 '26 02:02

VisioN


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.

like image 39
T.J. Crowder Avatar answered Feb 13 '26 02:02

T.J. Crowder



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!