Event.observe in a loop and variables
To put things in context, I'm loading a list of items via Ajax, creating a div with main info for each one and want to display details on page when clicking on it. So I have that code in my onSuccess :
items = transport.responseText.evalJSON(); // my list of objects that contains all the details I'll need for that page
for (var itemID in items)
{
newDiv = ... // Creating my div with main infos
$('myDiv').appendChild(newDiv);
// More code to make ever开发者_运维问答ything look pretty and that works fine
Event.observe(newDiv, 'click', function() { loadItem(itemID); });
}
loadItem is my function that will display all the item details. And my problem is that itemID isn't replace by its value when creating the observe event, so it always returns the same ID for all items.
Any idea how I can fix that ? I checked bind on prototype doc, that seemed to be made for those cases, but probably didn't get it, since it wouldn't work for me.
For a minimal-impact fix, replace your Event.observe
line with this:
Event.observe(newDiv, 'click', loadItem.curry(itemID));
Explanation:
In your original code, the event handler functions you're creating close over (have an enduring reference to) the itemID
variable, and so will use the value of that variable at of when the event handler is called, not as of when you assign it to the event. That value will be the last value that itemID
has in the loop — for all of the handler functions. More about closures here.
With the minimal-impact revised code, we use Prototype's curry
function, which will create a function for you that, when called, will call the underlying function with the arguments you gave curry
. (The name is from mathematics; Haskell Curry came up with the technique, though there are arguments he wasn't the first to do so.) We could do the same thing ourselves:
items = transport.responseText.evalJSON(); // my list of objects that contains all the details I'll need for that page
for (var itemID in items)
{
newDiv = ... // Creating my div with main infos
$('myDiv').appendChild(newDiv);
// More code to make everything look pretty and that works fine
Event.observe(newDiv, 'click', prepLoadItem(itemID));
}
function prepLoadItem(id) {
return function() {
loadItem(id);
};
}
...but because Prototype has a general-purpose function for it, we don't have to.
Off-topic: Is items
an array? If not, ignore this off-topic comment. If so, don't use for..in
to loop through it, or at least, not unless you take some precautions the code above doesn't to do it properly. Details here, but for..in
is not for looping through the indexes of an array; it's for looping through the properties of an object. Array objects may well have properties other than array indexes (and in fact, if you're using Prototype, they do.)
精彩评论