8 votes

Ajouter une classe à un et un seul élément d'une liste HTML lors d'un clic

J'ai une liste comme suit :

   Item 1
   Item 2
   Item 3

Et j'ai besoin d'ajouter la classe active à chaque élément individuellement et de la supprimer sur l'élément précédent lors du clic.

Quelque chose de similaire à cette question Vanilla JS remove class from previous selection cependant j'ai besoin d'utiliser une boucle For au lieu de ForEach en raison de la compatibilité du vieux navigateur.

J'ai essayé d'adapter cette réponse à mon exemple :

const items = document.querySelectorAll(".item");

for (let i = 0; i < items.length; i++) {
  const item = items[i];
  item.addEventListener("click", addActiveClass);

  for (let i = 0; i < items.length; i++) {
    const item = items[i];
    item.addEventListener("click", removeClass);
  }
}

function removeClass(e) {
  e.target.classList.remove("active");
}

function addActiveClass(e) {
  e.target.classList.add("active");
}

Mais ça ne fonctionne toujours pas comme prévu :(

7voto

Matt Ellen Points 5270

Je suppose que vous devez supprimer la classe active du reste des éléments, puis mettre à jour le bon élément. Cependant, je ne suis pas sûr que classList soit disponible dans les anciens navigateurs que vous cherchez à prendre en charge. Vous devriez vous renseigner sur le polyfill pour les anciens navigateurs.

const items = document.querySelectorAll(".item");

for (let i = 0; i < items.length; i++) {
  const item = items[i];
  item.addEventListener("click", changeActiveClass);
}

function changeActiveClass(e)
{
  for (let i = 0; i < items.length; i++) {
    const item = items[i];
    item.classList.remove('active');
  }
  e.target.classList.add('active');
}

7voto

ggorlen Points 5267

Votre approche actuelle ajoute plusieurs gestionnaires click à l'élément qui se déclenchent de manière séquentielle, en supprimant et ajoutant la classe plusieurs fois et avec des résultats imprévisibles en raison de la boucle imbriquée.

Les réponses existantes utilisent une solution en O(n) qui consiste à boucler sur tous les éléments de la liste, mais ce n'est pas nécessaire. Gardez simplement une référence du dernier élément sélectionné et supprimez la classe .active de cet élément s'il est défini:

const list = [...document.querySelectorAll("ul li")];
let selectedEl;

for (const el of list) {
  el.addEventListener("click", e => {
    selectedEl && selectedEl.classList.remove("active");
    selectedEl = e.target;
    e.target.classList.add("active");
  });
}

.active {
  background: yellow;
}

  Item 1
  Item 2
  Item 3

Une autre approche consiste à définir un attribut tabindex pour chaque élément, puis à ajouter des écouteurs pour les événements focus et blur (ou focusin/focusout si vous voulez une propagation) qui peuvent capturer de manière plus précise les sémantiques de sélection/désélection que vous essayez d'atteindre. Déplacer le focus n'importe où désélectionnera l'élément de la liste, contrairement à l'approche de l'événement click montrée ci-dessus.

for (const el of [...document.querySelectorAll("ul li")]) {
  el.addEventListener("focus", e => e.target.classList.add("active"));
  el.addEventListener("blur", e => e.target.classList.remove("active"));
}

.active {
  background: yellow;
  outline: none;
}

  Item 1
  Item 2
  Item 3

Si vous cherchez à prendre en charge les anciens navigateurs, vous pouvez essayer (non testé):

var list = document.getElementsByTagName("li");
var selectedEl;

for (var i = 0; i < list.length; i++) {
  list[i].addEventListener("click", function (e) {
    if (selectedEl) {
      selectedEl.className = selectedEl.className
        .split(/\s+/)
        .filter(function (e) { e !== "active"; })
        .join(" ");
    }

    selectedEl = e.target;
    e.target.className += " active";
  });
}

.active {
  background: yellow;
}

  Item 1
  Item 2
  Item 3

ou

var list = document.getElementsByTagName("li");

for (var i = 0; i < list.length; i++) {
  list[i].addEventListener("focusin", function (e) {
    e.target.className += " active";
  });
  list[i].addEventListener("focusout", function (e) {
    e.target.className = e.target.className
      .split(/\s+/)
      .filter(function (e) { e !== "active"; })
      .join(" ");
  });
}

.active {
  background: yellow;
  outline: none;
}

  Item 1
  Item 2
  Item 3

4voto

George Howarth Points 106

Vous devrez modifier un peu la boucle for :

for (let i = 0; i < items.length; i++) {
  const clickedItem = items[i];

  // Ajouter un écouteur d'événements à chaque élément
  clickedItem.addEventListener("click", function() {

    // Retirer la classe de tous les éléments
    for (let j = 0; j < items.length; j++) {
      const item = items[j];
      item.classList.remove("active");
    }

    // Ajouter la classe à l'élément cliqué
    clickedItem.classList.add("active");

  });
}

Remarquez que vous pourriez également utiliser une fonction fléchée, mais si vous visez la compatibilité ascendante, il est préférable d'utiliser function.

4voto

KooiInc Points 38845

Cela ressemble à un bon candidat pour la délégation d'événements. Le code pourrait être réécrit de la manière suivante

document.addEventListener("click", addActive);

function addActive(evt) {
  if (evt.target.classList.contains("item")) {
    // remove .active from all li.active
    const allItems = document.querySelectorAll(".active");
    for (let i=0; i

`li.item { cursor: pointer; } .active { color: red; }

   Item 1
   Item 2
   Item 3`

3voto

Stefano Giometti Points 268

Vous pourriez essayer quelque chose comme ceci :

const items = document.querySelectorAll(".item");

for (let i = 0; i < items.length; i++) {
    const item = items[i];
    item.addEventListener("click", toggleActive);
}

function cleanActiveClasses() {
    for (let i = 0; i < items.length; i++) {
        const item = items[i];
        item.className = item.className.replace(/(?:^|\s)active(?!\S)/g , '')
    }
}

function toggleActive(e) {
    cleanActiveClasses();
    e.target.className += " active";
}

.active {
  color: red;
}

   Item 1
   Item 2
   Item 3

Ajouté un peu de css juste pour vous donner un retour visuel.

En résumé : juste un écouteur sur l'élément DOM, qui nettoie chaque élément avant d'attribuer la nouvelle classe.

J'espère que cela aide.

EDIT : Comme vous avez demandé ceci pour la compatibilité ascendante, les anciennes versions d'IE ne prennent pas en charge classList. J'ai édité une nouvelle version utilisant className et une regex pour supprimer la classe active.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X