Добавляйте события в новые узлы

Я пытался добавить события click к серии divs, используя цикл for. divs создаются и загружаются динамически. Каждый div должен вызывать свою собственную функцию обратного вызова. Но кажется, что каждый div привязан к конечному прослушивателю событий и вызывает функцию обратного вызова конечного прослушивателя событий.

Ниже мой основной код:

for(index=0; index<divs.length; index++) {
  divs[index].addEventListener("click", function(){console.log(divs[index].getAttribute("id"));}, true); //capture click event
}

При клике каждый div отображает только идентификатор конечного div.


person Asur    schedule 16.01.2012    source источник
comment
прочитайте о закрытии javascript и отредактируйте свой код. Это проблема переменной области видимости.   -  person DhruvPathak    schedule 16.01.2012


Ответы (3)


Ответ Фила предлагает хорошее решение для опубликованного вами конкретного кода (+1), но не не объясняйте, в чем проблема с вашим исходным кодом.

Проблема в том, что замыкания обработчика событий получают постоянную ссылку на переменную index, а не ее копию на момент их создания. Таким образом, все они видят окончательное значение, которое имеет index (divs.length). Например, этот код

for (index = 0; index < 4; ++index) {
    setTimeout(function() {
        console.log(index);
    }, 100);
}

... будет регистрировать «4» четыре раза, когда происходит тайм-аут, а не «0», «1», «2» и «3».

Чтобы исправить это в общем случае, когда вы хотите убедиться, что ваш обработчик закрывается по определенному значению, используйте фабричную функцию, которая генерирует для вас функцию обработчика событий, где обработчик событий закрывается по аргументу, который вы передаете фабричной функции вместо аргумента. переменная цикла:

for(index=0; index<divs.length; index++) {
    divs[index].addEventListener("click", createHandler(divs[index], true); //capture click event
}

function createHandler(div) {
    return function(){
        console.log(div.getAttribute("id"));
    };
}

Там обработчик событий закрывается через div, что не меняется.

Замыкания — одна из самых мощных функций JavaScript. Как только вы поймете, как они работают (а на самом деле они намного проще, чем думают люди), вы сможете использовать их с действительно хорошим эффектом. Подробнее: Замыкания не сложны< /а>

person T.J. Crowder    schedule 16.01.2012
comment
+1 за отличное описание проблемы. Ваш метод createHandler также будет работать в IE (‹ 9) attachEvent, тогда как мой не будет (поддержка this нарушена) - person Phil; 16.01.2012
comment
@Т.Дж. Закрытие Crowder интересно, спасибо - person Asur; 17.01.2012

Это связано с тем, что вы используете divs[index] в обработчике событий, который после завершения цикла устанавливается на последний элемент.

Вместо этого используйте this. Кроме того, вы можете использовать один обработчик событий вместо создания множества одинаковых замыканий, например

function logId(e) {
    console.log(this.getAttribute("id"));
}

... и в вашем цикле ...

divs[index].addEventListener("click", logId, false);

См. https://developer.mozilla.org/en/DOM/element.addEventListener#Memory_issues

person Phil    schedule 16.01.2012

Вы можете использовать ключевое слово this, как указал @phil.

Но, чтобы объяснить, в чем заключалась ваша проблема, рассмотрим это закрытие:

for(index=0; index<divs.length; index++) {
  (function(index){
      divs[index].addEventListener("click", function(){
          console.log(divs[index].getAttribute("id"));
      }, true);
  })(index);
}

Теперь каждая функция получает собственную копию переменной index. В вашем коде все они используют одну и ту же переменную; поэтому, в конце концов, index будет равно divs.length -1 независимо от того, какой div на самом деле нажимается.

person Joseph Silber    schedule 16.01.2012
comment
ваша иллюстрация тоже работает, но я до сих пор не понимаю, что такое (функция (аргумент) { операторы }) (аргумент), спасибо за вашу помощь - person Asur; 17.01.2012