#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*/
));