#javascript
Вопрос:
Я попытался добавить «активный класс», который изменит цвет элемента навигации (отображается с помощью тегов li), когда пользователь нажимает на него. Для этого я создаю функцию для удаления активного класса, если таковой имеется во всех элементах li. После этого, когда появится щелчок по элементу navi, я добавлю активный класс к этому элементу. Проблема в том, что при запуске моего кода вместо того, чтобы только один элемент имел «активный» класс, все элементы имеют. Я нашел много решений этой проблемы, но большинство из них используют jQuery, о библиотеке которого я ничего не знаю. Я надеюсь, что кто-нибудь сможет указать на мои ошибки в коде ниже. Спасибо!
// Find all li tags
const liTags = document.querySelectorAll('li');
// Function to remove the current element has active class
function RemoveActive() {
for (let i = 0; i < liTags.length; i ) {
const currentActiveClass = document.querySelector('.active');
// Remove active class in the current li element
if (currentActiveClass != null) {
liTags[i].classList.remove('active');
}
}
}
// Add the active class to the clicked item
for (let i = 0; i < liTags.length; i ) {
liTags[i].addEventListener('click', function() {
RemoveActive;
liTags[i].classList.add('active');
})
}
Комментарии:
1. вы пытаетесь сделать что-то вроде переключателя ? где из всех можно выбрать только 1 предмет ?
2. Независимо от того, что вызвало неисправность, переосмыслите весь подход. Прямо сейчас каждый элемент LI , доступный ниже
document
уровня во время запроса, имеет свою собственную обработку щелчков. И хотя это можно сделать (вместо делегирования событий), возникает гораздо более серьезный вопрос. Есть ли во всем документе только один неупорядоченный список? Если нет, имейте в виду, что текущий подход будет работать в активных состояниях разных списков … означает, что любой щелчок элемента из случайного списка удаляет имяactive
класса (если оно существует) любого другого элемента списка из других списков.3. это все, что мне нужно. Я многому научился у вас, ребята. Спасибо.
Ответ №1:
Как вы можете видеть из моего примера, я добавляю classList.contains
вместо элемента проверки, тогда у вас есть ошибка опечатки ()
в функции
// Find all li tags
const liTags = document.querySelectorAll('li');
function RemoveActive() {
for (let i = 0; i < liTags.length; i ) {
if (liTags[i].classList.contains('active')) {
liTags[i].classList.remove('active');
}
}
}
for (let i = 0; i < liTags.length; i ) {
liTags[i].addEventListener('click', function() {
RemoveActive();
liTags[i].classList.add('active');
})
}
.active{
background-color:red;
}
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
Вместо использования remove
, и add
вы можете использовать toggle
, как:
// Find all li tags
const liTags = document.querySelectorAll('li');
function RemoveActive() {
const li = document.querySelector('li.active')
if (li) {
li.classList.toggle("active");
}
}
for (let i = 0; i < liTags.length; i ) {
liTags[i].addEventListener('click', function() {
RemoveActive();
liTags[i].classList.toggle('active');
})
}
.active {
background-color: red;
}
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
Комментарии:
1. Спасибо, что ответили. Это работает!!!
Ответ №2:
Я полагаю, что ваша ошибка была RemoveActive; там, где она должна была быть RemoveActive (), но подумал, что я потрачу время на рефакторинг кода.
Я бы посоветовал использовать верблюжий регистр для имен функций removeActive() и назвать их более описательным именем, чем «RemoveActive», так как это облегчит дальнейшую разработку / понимание того, что эта функция делает по мере роста программы.
const navigationItems = document.querySelectorAll('li');
function toggleActiveNavItem() {
navigationItems.forEach(item => {
item.addEventListener("click", function() {
addClickEventToNavigation(item)
}
}
}
function addClickEventToNavigation(item) {
// Remove active from every navigation item
navigationItems.forEach(individualNavigationItem =>{
// Other than the one passed to the function as having been clicked
if (individualNavigationItem != item) {
individualNavigationItem.classList.remove("active");
}
// If the clicked item does not have the active class, add it
if (!item.classList.contains("active")) {
individualNavigationItem.classList.add("active");
}
});
}
Ответ №3:
вы должны следовать этой практике для чистого и меньшего количества кода
var root = document.querySelector(".root")
root.addEventListener("click",e=>{
var t = e.target,
li = t.closest("li")
if(li){
root.querySelectorAll("li").forEach(each=>each.classList.remove("active"))
li.classList.add("active")
}
})
.active {
background:blue;
color:white;
}
.root li {
padding:2px;
cursor:pointer;
}
<ul class="root">
<li class="active">items</li>
<li>items</li>
<li>items</li>
<li>items</li>
<li>items</li>
<li>items</li>
<li>items</li>
</ul>
Комментарии:
1. Спасибо, ваш код довольно сложен для меня. Я думаю, что мне нужно учиться шаг за шагом.
Ответ №4:
Из приведенного выше прямого комментария к вопросу ОП …
«Независимо от того, что вызвало неисправность, переосмыслите весь подход. Прямо сейчас каждый элемент LI, доступный ниже
document
уровня во время запроса, имеет свою собственную обработку щелчков. И хотя это можно сделать (вместо делегирования событий), возникает гораздо более серьезный вопрос. Есть ли во всем документе только один неупорядоченный список? Если нет, имейте в виду, что текущий подход будет работать в активных состояниях разных списков … означает, что любой щелчок элемента из случайного списка удаляет имяactive
класса (если оно существует) любого другого элемента списка из других списков».
Следующий пример приведен только в демонстрационных целях, чтобы показать, чего можно достичь с помощью подхода, основанного на делегировании событий …
function getNestedListRoot(listNode) {
let elmNode = listNode;
while (listNode = listNode?.parentNode.closest('ol, ul')) {
elmNode = listNode;
}
return elmNode;
}
function handleNestedListItemsActiveState({ target }) {
// the LI element closest to the `click` source.
const srcLiElm = target.closest('li');
// guard
if (srcLiElm === null) {
return;
}
// the LI element's un/ordered list parent.
const listNode = srcLiElm.parentNode;
// the top most un/ordered list node
// of a nested list structure.
const listRoot = getNestedListRoot(listNode);
listRoot
.querySelectorAll('li')
.forEach(elmNode =>
// de'active'ate every LI element
// within the nested list structure.
elmNode.classList.remove('active')
);
let liElm = srcLiElm;
while (liElm) {
// follow the path of directly nested
// LI elements from the current inner to the
// most outer LI element and 'active'ate each.
liElm.classList.add('active');
liElm = liElm.parentNode.closest('li');
}
}
function initialize() {
document
.querySelectorAll('ol, ul')
.forEach(elmNode =>
elmNode.addEventListener('click', handleNestedListItemsActiveState)
)
}
initialize();
body { margin: -2px 0 0 0; }
ol, ul { margin: 0 0 10px 0; }
ol ul, ul ol, ol ol, ul ul { margin: 0; }
li { margin: 0 0 0 -20px; font-size: 12px; }
li.active,
li.active li.active { color: #fc0; }
li.active li { color: initial; }
<ol>
<li class="active">
OL_A-a
<ul>
<li>
OL_A-a__UL_A-a
</li>
<li class="active">
OL_A-a__UL_A-b
<ol>
<li>
OL_A-a__UL_A-b__OL_B-a
</li>
<li>
OL_A-a__UL_A-b__OL_B-b
</li>
<li class="active">
OL_A-a__UL_A-b__OL_B-c
</li>
</ol>
</li>
<li>
OL_A-a__UL_A-c
</li>
</ul>
</li>
<li>
OL_A-b
</li>
<li>
OL_A-c
<ul>
<li>
OL_A-c__UL_B-a
</li>
<li>
OL_A-c__UL_B-b
</li>
<li>
OL_A-c__UL_B-c
</li>
</ul>
</li>
</ol>
<ol>
<li>
OL_A-a
<ul>
<li>
OL_A-a__UL_A-a
</li>
<li>
OL_A-a__UL_A-b
<ol>
<li>
OL_A-a__UL_A-b__OL_B-a
</li>
<li>
OL_A-a__UL_A-b__OL_B-b
</li>
<li>
OL_A-a__UL_A-b__OL_B-c
</li>
</ol>
</li>
<li>
OL_A-a__UL_A-c
</li>
</ul>
</li>
<li>
OL_A-b
</li>
<li class="active">
OL_A-c
<ul>
<li class="active">
OL_A-c__UL_B-a
</li>
<li>
OL_A-c__UL_B-b
</li>
<li>
OL_A-c__UL_B-c
</li>
</ul>
</li>
</ol>