1

Уважаемые, нужен ваш опыт.

Есть многоуровневое меню, где к классам присваиваются дополнительные классы, через скрипт. Сам скрипт парсит код и создает массив, к элементам, которого присваиваются при клике дополнительные классы. Все бы хорошо, НО 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 Answers2

2

На самом деле события повешались на все элементы. Но когда дело дойдет до вызова функции, а это происходит после выхода из цикла, 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.

1

Про let все правильно. Но здесь можно обойтись без замыкания:

      thisArrow.addEventListener('click', function(){
        this.nextElementSibling.classList.toggle('open');
        this.classList.toggle('active');
      });