Как добавить css-класс, активный для пунктов меню, используя только код javascript?

#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>