I have a for loop running in javascript. In this loop I am creating a list item and binding a click event to it. When I click this list item I want it to call a function with the data from the current loop object as a parameter.
The problem is, no matter what list item I click. The data that is being passed as the parameter is the last element of the object I am looping rather than the current one that is being clicked.
for(e in data) {
var suggestItem = $('<li>'+ data[e]['name'] +'</li>');
suggestItem.click(function() {
$(this).addClass('activeSuggestion');
suggestSelect(suggestField, data[e]);
});
suggestList.append(suggestItem);
}
I think I understand why this happens but not sure how I should handle it.
Variable i Always Has The Last IndexBy the time the for loop reaches the last iteration, the i variable will end up holding the last index value. That's why the output will always be the last index, in my case, 5.
Approach 1: Using the for loop: The HTML elements can be iterated by using the regular JavaScript for loop. The number of elements to be iterated can be found using the length property. The for loop has three parts, initialization, condition expression, and increment/decrement expression.
jQuery is a fast, small, and feature-rich JavaScript library. It makes things like HTML document traversal and manipulation, event handling, animation, and Ajax much simpler with an easy-to-use API that works across a multitude of browsers.
This is a classic Javascript closure question. When the click event is triggered (when you click on something), the loop has already finished executing. The value of e
is whatever the last key in data
is. The solution to the problem is to create a new scope inside of your loop.
for(var e in data) {
(function(datum) {
suggestList.append(
$('<li>' + datum.name + '</li>')
.click(function() {
$(this).addClass('activeSuggestion');
suggestSelect(suggestField, datum);
});
);
})(data[e]);
}
Javascript is function scoped, so whenever a new function is encountered, a new scope is created. The above code "traps" the value of data[e]
into datum
because it passes it as a parameter to the function. Another way to code it which may be less confusing:
for(var e in data) {
(function() {
var datum = data[e];
suggestList.append(
$('<li>' + datum.name + '</li>')
.click(function() {
$(this).addClass('activeSuggestion');
suggestSelect(suggestField, datum);
});
);
})();
}
It doesn't pass in a parameter to the function, but because Javascript is function scoped, every time the click event is triggered it will look for where datum
was assigned.
Also please note the for(VAR e in data)
which will stop e
from becoming a global variable.
You need to break the closure over e
.
Since you're using jQuery, the easiest way would be to save the current value of e
with jQuery's data
function. Since all function parameters in JavaScript are passed by value, this effectively breaks the closure; now your click handler will work with what the value of e
is when the handler was created, and not the value e
holds when the loop ends.
for(e in data) {
var suggestItem = $('<li>'+ data[e]['name'] +'</li>');
suggestItem.data('savedE', e).click(function() {
$(this).addClass('activeSuggestion');
suggestSelect(suggestField, data[$(this).data('savedE')]);
});
suggestList.append(suggestItem);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With