Wie man durch Klassen eines HTML-Elements schaltet

Avatar of Chris Coyier
Chris Coyier am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Nehmen wir an, Sie haben drei HTML-Klassen und ein DOM-Element sollte zu jeder Zeit nur eine davon haben

<div class="state-1"></div>
<div class="state-2"></div>
<div class="state-3"></div>

Nun besteht Ihre Aufgabe darin, sie zu rotieren. Das heißt, durch Klassen eines HTML-Elements zu schalten. Wenn ein bestimmtes Ereignis eintritt, und das Element die Klasse state-1 hat, entfernen Sie state-1 und fügen state-2 hinzu. Wenn es die Klasse state-2 hat, entfernen Sie diese und fügen state-3 hinzu. Bei dem letzten Zustand entfernen Sie ihn und schalten zurück zu state-1.

Example of how to Cycle Through Classes on an HTML Element. Here a large <button> with an <svg> inside cycles through state-1, state-2, and state-3 classes, turning from red to yellow to green.

Es ist erwähnenswert, dass wir hier von 3+ Klassen sprechen. Das DOM hat eine Funktion .classList.toggle(), sogar eine, die eine Bedingung als zweiten Parameter nimmt, aber diese ist hauptsächlich in einer Zwei-Klassen-Ein/Aus-Situation nützlich, nicht zum Durchschalten von Klassen.

Warum? Dafür gibt es eine Reihe von Gründen. Das Ändern eines Klassennamens gibt Ihnen viel Macht, Dinge im DOM neu zu gestalten, und Zustandsverwaltung wie diese ist ein Eckpfeiler der modernen Webentwicklung. Aber um es spezifisch zu machen: In meinem Fall wollte ich FLIP-Animationen durchführen, bei denen ich ein Layout ändern und eine Tween-Animation zwischen den verschiedenen Zuständen auslösen würde.

Vorsicht bei bestehenden Klassen! Ich habe einige Ideen gesehen, die .className überschrieben haben, was nicht freundlich zu anderen Klassen ist, die sich möglicherweise auf dem DOM-Element befinden. All diese sind "sichere" Entscheidungen, um auf diese Weise durch Klassen zu schalten.

Da dies Programmierung ist, gibt es *viele* Möglichkeiten, dies zu tun. Lassen Sie uns eine Reihe davon behandeln — *zum Spaß.* Ich habe über dieses Thema getwittert, daher stammen viele dieser Lösungen von Leuten, die sich an dieser Diskussion beteiligt haben.

Eine ausführliche if/else-Anweisung zum Durchschalten von Klassen

Das habe ich anfangs gemacht, um durch Klassen zu schalten. So funktioniert mein Gehirn. Schreibe einfach sehr spezifische Anweisungen dafür, was genau passieren soll.

if (el.classList.contains("state-1")) {
  el.classList.remove("state-1");
  el.classList.add("state-2");
} else if (el.classList.contains("state-2")) {
  el.classList.remove("state-2");
  el.classList.add("state-3");
} else {
  el.classList.remove("state-3");
  el.classList.add("state-1");
}

Die Ausführlichkeit stört mich hier nicht, da sie für mich sehr klar macht, was passiert, und es einfach sein wird, zu diesem Code zurückzukehren und ihn zu "analysieren", wie man so schön sagt. Sie könnten die Ausführlichkeit als Problem betrachten – sicherlich gibt es eine Möglichkeit, mit weniger Code durch Klassen zu schalten. Aber ein größeres Problem ist, dass es nicht sehr erweiterbar ist. Es gibt keine Anzeichen von Konfiguration (z. B. die Namen der Klassen einfach ändern) oder eine einfache Möglichkeit, Klassen hinzuzufügen oder zu entfernen.

Wir könnten zumindest Konstanten verwenden

const STATE_1 = "state-1";
const STATE_2 = "state-2";
const STATE_3 = "state-3";

if (el.classList.contains(STATE_1)) {
  el.classList.remove(STATE_1);
  el.classList.add(STATE_2);
} else if (el.classList.contains(STATE_2)) {
  el.classList.remove(STATE_2);
  el.classList.add(STATE_3);
} else {
  el.classList.remove(STATE_3);
  el.classList.add(STATE_1);
}

Aber das ist nicht wild anders oder besser.

RegEx von der alten Klasse entfernen, Zustand erhöhen, dann wieder hinzufügen

Diese Idee stammt von Tab Atkins. Da wir das Format der Klasse kennen, state-N, können wir danach suchen, die Zahl extrahieren, mit einem kleinen Ternäroperator erhöhen (aber nicht höher als der höchste Zustand) und dann die Klassen hinzufügen/entfernen, um sie zu durchschalten.

const oldN = +/\bstate-(\d+)\b/.exec(el.getAttribute('class'))[1];
const newN = oldN >= 3 ? 1 : oldN+1;
el.classList.remove(`state-${oldN}`);
el.classList.add(`state-${newN}`);

Den Index der Klasse finden und dann entfernen/hinzufügen

Viele Techniken zum Durchschalten von Klassen konzentrieren sich darauf, im Voraus ein Array von Klassen einzurichten. Dies dient als Konfiguration für das Durchschalten von Klassen, was meiner Meinung nach eine intelligente Vorgehensweise ist. Sobald Sie das haben, können Sie die relevanten Klassen zum Hinzufügen und Entfernen finden. Dies ist von Christopher Kirk-Nielsen.

const classes = ["state-1", "state-2", "state-3"];
const activeIndex = classes.findIndex((c) => el.classList.contains(c));
const nextIndex = (activeIndex + 1) % classes.length;

el.classList.remove(classes[activeIndex]);
el.classList.add(classes[nextIndex]);

Christopher hatte auch eine nette Idee, um die Hinzufügen/Entfernen-Technik kürzer zu gestalten. Es stellt sich heraus, dass es dieselbe ist…

el.classList.remove(classes[activeIndex]);
el.classList.add(classes[nextIndex]);

// Does the same thing.
el.classList.replace(classes[activeIndex], classes[nextIndex]);

Mayank hatte eine ähnliche Idee zum Durchschalten von Klassen, indem er die Klasse in einem Array findet, nur anstatt classList.contains() zu verwenden, prüft man die aktuell auf dem DOM-Element befindlichen Klassen mit denen im Array.

const states = ["state-1", "state-2", "state-3"];
const current = [...el.classList].find(cls => states.includes(cls));
const next = states[(states.indexOf(current) + 1) % states.length];
el.classList.remove(current);
el.classList.add(next);

Variationen davon waren die häufigste Idee. Hier ist Jheys und hier ist Mike Wagz, die Funktionen zum Vorwärts- und Rückwärtsgehen einrichten.

Kaskadierende Replace-Anweisungen

Wo wir gerade von der replace-API sprechen: Chris Calo hatte eine clevere Idee, bei der man sie mit dem or-Operator verkettet und sich darauf verlässt, dass sie true/false zurückgibt, wenn sie funktioniert oder nicht. Man führt also alle drei aus, und einer davon wird funktionieren!

 el.classList.replace("state-1", "state-2") ||
 el.classList.replace("state-2", "state-3") ||
 el.classList.replace("state-3", "state-1");

Nicolò Ribaudo kam zu demselben Schluss.

Einfach durch Klassennummern schalten

Wenn Sie im Voraus eine 1 konfiguriert hätten, könnten Sie durch die Klassen 1-3 schalten und sie basierend darauf hinzufügen/entfernen. Dies ist von Timothy Leverett, der in demselben Tweet eine weitere ähnliche Option auflistet.

// Assumes a `let s = 1` upfront
el.classList.remove(`state-${s + 1}`);
s = (s + 1) % 3;
el.classList.add(`state-${s + 1}`);

Verwenden Sie stattdessen data-* Attribute

Data-Attribute haben die gleiche spezifische Kraft, daher habe ich damit kein Problem. Sie sind vielleicht sogar klarer in Bezug auf die Zustandsbehandlung, aber noch besser, sie haben eine spezielle API, die sie angenehm zu manipulieren macht. Munawwar Firoz hatte eine Idee, die dies auf eine einzige Zeile reduziert.

el.dataset.state = (+el.dataset.state % 3) + 1

Eine Zustandsmaschine für Data-Attribute

Auf David Khourshid kann man sich verlassen, dass er mit einer Zustandsmaschine bereit ist.

const simpleMachine = {
  "1": "2",
  "2": "3",
  "3": "1"
};
el.dataset.state = simpleMachine[el.dataset.state];

Sie werden fast sicher eine Funktion wollen

Geben Sie sich selbst etwas Abstraktion, richtig? Viele der Ideen wurden so umgesetzt, aber bisher habe ich es ausgelagert, um mich auf die Idee selbst zu konzentrieren. Hier lasse ich die Funktion drin. Diese ist von Andrea Giammarchi, bei der eine eindeutige Funktion zum Durchschalten von Klassen im Voraus eingerichtet wird, und dann rufen Sie sie bei Bedarf auf.

const rotator = (classes) => ({ classList }) => {
  const current = classes.findIndex((cls) => classList.contains(cls));
  classList.remove(...classes);
  classList.add(classes[(current + 1) % classes.length]);
};

const rotate = rotator(["state-1", "state-2", "state-3"]);
rotate(el);

Ich habe von Kyle Simpson gehört, der dieselbe Idee hatte, fast wortwörtlich.

Andere?

Es gab weitere Ideen in den Antworten auf meinen ursprünglichen Tweet, aber es sind, soweit ich das beurteilen kann, Variationen dessen, was ich oben bereits geteilt habe. Entschuldigung, wenn ich Ihre übersehen habe! Fühlen Sie sich frei, Ihre Idee hier in den Kommentaren erneut zu teilen. Ich sehe, dass niemand switch-Anweisungen verwendet hat — das könnte eine Möglichkeit sein!

David Desandro ging sogar so weit, ein Video aufzunehmen, was wunderbar ist, da es die Konzepte langsam weiter und weiter abstrahiert, bis es prägnant, aber dennoch lesbar und viel flexibler ist.

Und hier ist ein Demo-Pen mit dem gesamten Code für jedes Beispiel darin. Sie sind nummeriert, also um ein anderes auszuprobieren, kommentieren Sie das, das auskommentiert ist, aus und kommentieren Sie ein anderes Beispiel ein.