Варианты решения с помощью делегирования событий:
Вешаем обработчик клика на корневой элемент и слушаем событие клика. Если клик произошел на элементе с нужным классом, тогда проходим циклом по всем элементам с этим классом, далее удаляем у них "активные" классы и добавляем их текущему элементу:
const elements = document.querySelectorAll(".value-item");
const stateClasses = ["value-active", "basic__shadow"];
document.addEventListener("click", ({ target }) => {
if (!target.classList.contains("value-item")) return;
elements.forEach(v => v.classList.remove(...stateClasses));
target.classList.add(...stateClasses);
});
Или:
Почти всё тоже самое что и в первом варианте - за исключением того в этот раз будем работать без цикла. Находим элемент с "активными" классами и удаляем их, после чего добавим эти классы текущему элементу, на котором и произошел клик:
const stateClasses = ["value-active", "basic__shadow"];
document.addEventListener("click", ({ target }) => {
if (!target.classList.contains("value-item")) return;
const activeItem = document.querySelector(".value-active");
activeItem && activeItem.classList.remove(...stateClasses);
target.classList.add(...stateClasses);
});
Варианты решения с помощью цикла forEach:
const elements = document.querySelectorAll(".value-item");
const stateClasses = ["value-active", "basic__shadow"];
const toggleActiveElement = element => {
element.addEventListener("click", () => {
elements.forEach(n => n.classList.remove(...stateClasses));
element.classList.add(...stateClasses);
});
};
elements.forEach(v => toggleActiveElement(v));
Или:
const elements = document.querySelectorAll(".value-item");
const stateClasses = ["value-active", "basic__shadow"];
const toggleActiveElement = element => {
element.addEventListener("click", () => {
document.querySelector(".value-active")?.classList.remove(...stateClasses);
element.classList.add(...stateClasses);
});
};
elements.forEach(v => toggleActiveElement(v));
Однако у этих двух вариантов есть небольшие минусы и для таких целей как переключение активного класса при клике, хорошо подходит делегирование событий. Но почему в большинстве случаев не стоит использовать цикл для решения подобных задач?
1. Вы заставляете браузер искать все элементы с определенным классом на странице.
2. Добавляется обработчик на каждый элемент, хотя возможно пользователь и не кликнет на этот элемент.
3.
addEventListener добавленный через цикл к каждому элементу определенного
NodeList - не будет работать на динамически добавленных элементах. Разумеется если не добавить к ним обработчики, после создания, но это конечно так себе работа.
Возможно есть и другие побочные эффекты и/или минусы у такого решения задачи.
Обычно добавление обработчиков в цикле используется в связи с недостатком опыта или просто потому что данный способ в той или иной степени решает поставленную задачу и делать по другому тупо нет желания или смысла. Но даже если отказ от подобных решений и является просто напросто экономией на спичках и/или чем-то там ещё, в любом случае лучше использовать инструменты и функции по назначению, а не потому что просто так хочется или "удобно".
Правда всё же есть исключения когда добавление обработчиков в цикле оправданно и будет наиболее подходящим способом решить задачу чем например через делегирование событий.