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.
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.
Share Improve this question asked Dec 26, 2011 at 19:30 Justin CarlsonJustin Carlson 2,7102 gold badges20 silver badges19 bronze badges 1- 1 possible duplicate of jQuery Event Handler created in loop – Pointy Commented Dec 26, 2011 at 19:34
2 Answers
Reset to default 5This 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 being 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);
}