Почему addEventListener в массиве срабатывает только в последнем элементе?
Уважаемые, нужен ваш опыт.
Есть многоуровневое меню, где к классам присваиваются дополнительные классы, через скрипт. Сам скрипт парсит код и создает массив, к элементам, которого присваиваются при клике дополнительные классы. Все бы хорошо, НО addEventListener, навешивает события, только на последний элемент массива. Подскажите как это исправить?
Строго не корите, учусь только.
var arrow=document.querySelectorAll('.arrow');
for (var i=0; i<arrow.length; i++){
var thisLink=arrow[i].previousElementSibling;
var subMenu=arrow[i].nextElementSibling;
var thisArrow=arrow[i];
thisLink.classList.add('parent');
thisArrow.addEventListener('click', function(){
subMenu.classList.toggle('open');
thisArrow.classList.toggle('active');
});}
<nav class="main-nav">
<ul class="main-nav__list">
<li>
<a href="#" class="main-nav__link">Первый уровень</a>
<span class="menu-nav-arrow arrow"></span>
<ul class="sub-main-nav__list">
<li>
<a href="#" class="sub-main-nav__link">Второй уровень</a>
</li>
<li>
<a href="#" class="sub-main-nav__link">Второй уровень</a>
<!-- <span class="sub-menu-nav-arrow arrow"></span> -->
<ul class="sub-sub-main-nav__list">
<li>
<a href="#" class="sub-sub-main-nav__link">Третий уровень</a>
</li>
<li>
<a href="#" class="sub-sub-main-nav__link">Третий уровень</a>
</li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#" class="main-nav__link">Первый уровень</a>
<span class="menu-nav-arrow arrow"></span>
<ul class="sub-main-nav__list">
<li>
<a href="#" class="sub-main-nav__link">Второй уровень</a>
</li>
</ul>
</li>
<li>
<a href="#" class="main-nav__link">Первый уровень</a>
</li>
</ul>
</nav>
Ответы (2 шт):
Про let все правильно. Но здесь можно обойтись без замыкания:
thisArrow.addEventListener('click', function(){
this.nextElementSibling.classList.toggle('open');
this.classList.toggle('active');
});
На самом деле события повешались на все элементы.
Но когда дело дойдет до вызова функции, а это происходит после выхода из цикла,
thisArrow внутри нее будет ссылаться на последний элемент arrow[arrow.length-1],
так как объявлен через var, который не имеет блочной области видимости
Это почти то же самое, что записать так
var foo = 5
var foo = 6
... и пытаться получить доступ к первому foo = 5
const array = []
for (var item of [1, 2, 3]) {
let i = item
// Уже на второй итерации, первый item "затрется" вторым и т.д.
// и никогда не будед доступен
array.push(() => console.log(`Итерация номер ${i}: item ${item}`))
}
// В этом вызове мы обращаемся к item
array.forEach((f) => f())
// Этот item виден даже здесь - за скобками {}
console.log(item)
// Вариант с let|const
const array2 = []
for (let item2 of [1, 2, 3]) {
// let в отличии от var сохраняется для каждой итерации
let i = item2
array2.push(() => console.log(`Итерация номер ${i}: item ${item2}`))
}
array2.forEach((f) => f())
// И за скобками let не доступен и вызовет ошибку
try {
console.log(item2)
} catch (e) {
console.log(e.message) // item2 is not defined
}
Чтоб не делать ошибок, лучше никогда не использовать var.