Event.observe в цикле и переменных - PullRequest
2 голосов
/ 19 декабря 2010

Чтобы поместить вещи в контекст, я загружаю список элементов через Ajax, создаю div с основной информацией для каждого и хочу отображать детали на странице при нажатии на нее.Итак, у меня есть этот код в моем 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 everything look pretty and that works fine

    Event.observe(newDiv, 'click', function() { loadItem(itemID); });
}

loadItem - моя функция, которая будет отображать все детали элемента.И моя проблема в том, что itemID не заменяется его значением при создании события наблюдения, поэтому он всегда возвращает один и тот же идентификатор для всех элементов.

Есть идеи, как мне это исправить?Я проверил bind на прототипе документа, который, казалось, был сделан для этих случаев, но, вероятно, не получил его, так как он не будет работать для меня.

1 Ответ

5 голосов
/ 19 декабря 2010

Для исправления с минимальным воздействием замените строку Event.observe на следующую:

Event.observe(newDiv, 'click', loadItem.curry(itemID));

Пояснение:

В исходном коде создаваемые вами обработчики событий закрывают (имеют постоянную ссылку) переменную itemID и поэтому будут использовать значение этой переменной в момент, когда Обработчик события называется , а не тогда, когда вы назначаете его для события. Это значение будет последним значением, которое itemID имеет в цикле & mdash; для всех функций обработчика. Подробнее о замыканиях здесь.

В пересмотренном коде с минимальным воздействием мы используем функцию curry Prototype, которая создаст для вас функцию, которая при вызове будет вызывать базовую функцию с аргументами, которые вы дали curry , (Имя из математики ; Хаскелл Карри придумал эту технику, хотя есть аргументы, что он был не первым, кто это сделал.) Мы могли бы сделать то же самое сами:

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);
    };
}

... но поскольку у Prototype есть функция общего назначения, нам не нужно.

Не по теме : items это массив? Если нет, игнорируйте этот не по теме комментарий. Если это так, не не используйте for..in для его обхода или, по крайней мере, если вы не примете некоторые меры предосторожности, приведенный выше код не сделает это должным образом. Подробности здесь , но for..in - это , а не для циклического перемещения по индексам массива; это для просмотра свойств объекта. Объекты массива вполне могут иметь свойства, отличные от индексов массива (и фактически, если вы используете Prototype, они имеют.)

...