Как добавить прослушиватель событий с тем же классом?

#javascript #html #css

#javascript #HTML #css

Вопрос:

У меня есть два li с class="tree" в a ul , я хочу установить значение a height" ul.sub-menu , когда кто-то нажимает на его родительский li элемент. Но это не работает для второго li . Запустить фрагмент

 document.querySelector(".tree").addEventListener("click",  function () {
    var height = document.querySelector(".sub-menu").scrollHeight;   
    document.querySelector(".sub-menu").style.height = height   "px";
}); 
 .sub-menu {
    position: relative;
    background-color: aqua;
    width: 100px;
    height: 0;
    overflow: hidden;
    transition: height 0.3s;
        
} 
 <ul>
    <li class="tree">
        <a href="#">Item</a>
        <ul class="sub-menu">
            <li><a href="#">Sub Item1</a></li>
            <li><a href="#">Sub Item1</a></li>
            <li><a href="#">Sub Item1</a></li>
        </ul>
    </li>
    <li class="tree">
        <a href="#">Item</a>
        <ul class="sub-menu">
            <li><a href="#">Sub Item2</a></li>
            <li><a href="#">Sub Item2</a></li>
            <li><a href="#">Sub Item2</a></li>
        </ul>
    </li>
</ul> 

Комментарии:

1. Я думаю, вы можете использовать другой идентификатор для элементов.

2. Вероятно, вам следует использовать document.querySelectorAll() вместо document.querySelector() .

Ответ №1:

querySelector вернет только первый элемент.

Однако рекомендуется делегировать из ближайшего общего статического контейнера — это также намного элегантнее, чем перебирать все элементы для добавления EventListeners к каждому

 document.getElementById("topUl").addEventListener("click",  function (e) {
  const tgt = e.target.closest("li");
  if (tgt.classList.contains("tree")) {
    const sm = tgt.querySelector(".sub-menu");
    const height = sm.scrollHeight;   
    sm.style.height = height   "px";
  }  
}); 
 .sub-menu {
    position: relative;
    background-color: aqua;
    width: 100px;
    height: 0;
    overflow: hidden;
    transition: height 0.3s;
        
} 
 <ul id="topUl">
    <li class="tree">
        <a href="#">Item</a>
        <ul class="sub-menu">
            <li><a href="#">Sub Item1</a></li>
            <li><a href="#">Sub Item1</a></li>
            <li><a href="#">Sub Item1</a></li>
        </ul>
    </li>
    <li class="tree">
        <a href="#">Item</a>
        <ul class="sub-menu">
            <li><a href="#">Sub Item2</a></li>
            <li><a href="#">Sub Item2</a></li>
            <li><a href="#">Sub Item2</a></li>
        </ul>
    </li>
</ul> 

Ответ №2:

Все, что вам нужно сделать, это использовать querySelectorAll для выбора всех классов и добавления EventListener внутри цикла

    document.querySelectorAll(".tree").forEach(i => i.addEventListener(
        "click",/*your code here*/));
 

Ответ №3:

Решение с использованием forEach() метода.

 let tree = document.querySelectorAll(".tree");
let height = document.querySelectorAll(".sub-menu");

tree.forEach(function(tree_current, index) {
  tree_current.addEventListener("click",  function () {      
    height[index].style.height = height[index].scrollHeight   "px";
  });   
}) 
 .sub-menu {
    position: relative;
    background-color: aqua;
    width: 100px;
    height: 0;
    overflow: hidden;
    transition: height 0.3s;
        
} 
 <ul>
    <li class="tree">
        <a href="#">Item</a>
        <ul class="sub-menu">
            <li><a href="#">Sub Item1</a></li>
            <li><a href="#">Sub Item1</a></li>
            <li><a href="#">Sub Item1</a></li>
        </ul>
    </li>
    <li class="tree">
        <a href="#">Item</a>
        <ul class="sub-menu">
            <li><a href="#">Sub Item2</a></li>
            <li><a href="#">Sub Item2</a></li>
            <li><a href="#">Sub Item2</a></li>
        </ul>
    </li>
</ul> 

Комментарии:

1. Ах, да. в this.querySelector(".subMenu") любом случае неудобно индексировать свой путь в это

Ответ №4:

document.querySelector() возвращает первый объект, соответствующий запросу, а не все из них. Вы можете проверить их, вставив console.log(document.querySelector('.tree')); в начало вашего JS-файла, и вы увидите, что он регистрирует только ваш первый элемент. Если вы хотите добавить прослушиватель ко всем вашим элементам, соответствующим запросу, вам нужно использовать document.querySelectorAll() и перебирать этот массив с помощью forEach :

 document.querySelectorAll('.tree').forEach(el => el.addEventListener('click', () => {
    /* Code Here */
}));
 

Еще одна проблема, с которой вы столкнетесь в своем коде, заключается в том, что ваш другой селектор запросов также возвращает первое вхождение .sub-menu . Чтобы исправить это, вы можете изменить

 document.querySelectorAll('.tree').forEach(el => el.addEventListener('click', () => {
    /* Code Here */
}));
 

Для

 document.querySelectorAll('.tree').forEach(el => {
    const submenu = el.querySelector('.sub-menu');
    el.addEventListener('click', () => submenu.style.height = submenu.scrollHeight   "px");
});
 

Поскольку .sub-menu это дочерний элемент .tree в вашей иерархии, он будет искать только ваши .tree экземпляры .sub-menu и, следовательно, возвращать нужный вам элемент. Тогда ваш окончательный код будет выглядеть так:

 document.querySelectorAll('.tree').forEach(el => {
  const submenu = el.querySelector('.sub-menu');
  el.addEventListener('click', () => submenu.style.height = submenu.scrollHeight   "px");
}) 
 .sub-menu {
  position: relative;
  background-color: aqua;
  width: 100px;
  height: 0;
  overflow: hidden;
  transition: height 0.3s;
} 
 <ul>
  <li class="tree">
    <a href="#">Item</a>
    <ul class="sub-menu">
      <li><a href="#">Sub Item1</a></li>
      <li><a href="#">Sub Item1</a></li>
      <li><a href="#">Sub Item1</a></li>
    </ul>
  </li>
  <li class="tree">
    <a href="#">Item</a>
    <ul class="sub-menu">
      <li><a href="#">Sub Item2</a></li>
      <li><a href="#">Sub Item2</a></li>
      <li><a href="#">Sub Item2</a></li>
    </ul>
  </li>
</ul> 

РЕДАКТИРОВАТЬ: не повторяйтесь. Чтобы избежать необходимости поиска элемента при каждом нажатии кнопки, мы кэшируем его, как только инициализируем прослушиватель и ссылаемся на эту сохраненную переменную в обработчике кликов.

Комментарии:

1. ВЫСУШИТЕ, пожалуйста. Кэшируйте элемент.

2. @mplungjan хотел максимально приблизиться к исходному коду OP, просто исправив его. Вы правы, что лучше поощрять DRY, поэтому я отредактировал свой ответ, чтобы отразить его. Спасибо 🙂

3. Потому что я не вижу, чтобы он менялся во время выполнения. Конечно, если он изменится, то он должен быть внутри. Это зависит от использования OP этих списков

Ответ №5:

Просто используйте querySelectorAll, он выберет все классы и добавит событие щелчка внутри него.

    document.querySelectorAll(".tree").forEach(

       index => index.addEventListener("click",
        /*your code here*/

   ));