Так есть же функция toggle()?
Да, в самом Жабаскрипте есть такая функция, но она позволяет переключаться только между 1-2 классами.
А что делать, когда нужно переключаться между 3 и более классами? Давай разбираться
Наш красивый и полезный канал:3
Скажем, у вас есть три класса HTML, и элемент DOM должен иметь только один из них одновременно:
<div class="state-1"></div>
<div class="state-2"></div>
<div class="state-3"></div>
Теперь ваша задача — вращать их. То есть циклически перебирать классы HTML-элемента. Когда происходит какое-то событие, если на нем есть элемент state-1, удалите state-1и добавьте state-2. Если он есть state-2, удалите это и добавьте state-3. В последнем состоянии удалите его и вернитесь к state-1.
Примечательно, что здесь речь идет о классах 3+. В DOM есть .classList.toggle()функция, даже та, которая принимает условное выражение в качестве второго параметра, но она в первую очередь полезна в ситуации включения/выключения двух классов, а не циклического переключения между классами.
Почему? Есть ряд причин. Изменение имени класса дает вам много возможностей для изменения стиля элементов в DOM, а подобное управление состоянием является краеугольным камнем современной веб-разработки. Но если быть точным, в моем случае я хотел сделать анимацию FLIP , где я бы изменил макет и запускал анимацию движения между различными состояниями.
Будьте осторожны с существующими классами! Я видел некоторые идеи, которые перезаписывали .className, что недружественно по отношению к другим классам, которые могут быть в элементе DOM. Все это «безопасный» выбор для циклического прохождения классов таким образом.
Подробный оператор if/else для циклического перебора классов
Это то, что я сделал сначала, чтобы пройтись по классам. Так работает мой мозг. Просто напишите очень конкретные инструкции для того, что именно вы хотите, чтобы произошло:
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");
}
Я не возражаю против многословия здесь, потому что мне очень ясно, что происходит, и мне будет легко вернуться к этому коду и, как говорится, «рассуждать об этом». Многословие можно считать проблемой — наверняка есть способ перебирать классы с меньшим количеством кода. Но большая проблема в том, что он не очень расширяемый. Нет никакого подобия конфигураций (например, легко изменить имена классов) или простого способа добавить классы в партию или удалить их.
Мы могли бы использовать константы, по крайней мере:
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);
}
Но это не сильно отличается или лучше.
RegEx от старого класса, увеличение состояния, затем повторное добавление
Этот исходит от Таба Аткинса . Так как мы знаем формат класса, state-Nмы можем искать его, выдергивать число, использовать небольшую троицу для его увеличения (но не выше самого высокого состояния), а затем добавлять/удалять классы как способ циклического прохождения. их:
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}`);
Найдите индекс класса, затем удалите/добавьте
Куча методов для циклического переключения классов сосредоточена на настройке массива классов заранее. Это действует как конфигурация для циклического переключения классов, и я думаю, что это разумный способ сделать это. Получив это, вы можете найти соответствующие классы для их добавления и удаления. Это от Кристофера Кирка-Нильсена :
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]);
У Кристофера была хорошая идея сделать технику добавления/удаления короче. Оказывается, это одно и то же…
el.classList.remove(classes[activeIndex]);
el.classList.add(classes[nextIndex]);
// Does the same thing.
el.classList.replace(classes[activeIndex], classes[nextIndex]);
У Mayank была похожая идея для циклического переключения классов путем поиска класса в массиве, только вместо использования classList.contains(), вы проверяете классы, находящиеся в настоящее время в элементе DOM, с тем, что находится в массиве.
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);
Варианты этого были наиболее распространенной идеей. Вот Jhey и вот Mike Wagz, которые настраивают функции для движения вперед и назад.
Каскадные операторы замены
Говоря об этом replaceAPI, у Криса Кало была умная идея , когда вы связываете их с orоператором и полагаетесь на тот факт, что он возвращает true/false, работает он или нет. Итак, вы делаете все три, и один из них будет работать!
el.classList.replace("state-1", "state-2") ||
el.classList.replace("state-2", "state-3") ||
el.classList.replace("state-3", "state-1");
Просто пролистайте номера классов
Если вы предварительно настроили предварительную настройку 1, вы можете циклически переключаться между классами 1-3 и добавлять/удалять их на основе этого. Это от Тимоти Леверета , который перечисляет еще один подобный вариант в том же твите.
// Assumes a `let s = 1` upfront
el.classList.remove(`state-${s + 1}`);
s = (s + 1) % 3;
el.classList.add(`state-${s + 1}`);
Вместо этого используйте data-*атрибуты
Атрибуты данных имеют одинаковую силу специфичности, поэтому у меня нет проблем с этим. На самом деле они могут быть более понятными с точки зрения обработки состояния, но, что еще лучше, у них есть специальный API , которым приятно манипулировать. У Мунаввара Фироза была идея , которая сводится к одной строчке:
el.dataset.state = (+el.dataset.state % 3) + 1
Конечный автомат атрибутов данных
Вы можете рассчитывать на то, что Дэвид Хуршид подготовит конечный автомат:
const simpleMachine = {
"1": "2",
"2": "3",
"3": "1"
};
el.dataset.state = simpleMachine[el.dataset.state];
Вам почти наверняка понадобится функция
Дайте себе немного абстракции, не так ли? Многие идеи были написаны таким образом, но пока я переместил его, чтобы сосредоточиться на самой идее. Здесь я оставлю функцию. Это от Андреа Джаммарки , в котором уникальная функция для циклического переключения классов настраивается заранее, а затем вы вызываете ее по мере необходимости:
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);
Я слышал от Кайла Симпсона, у которого была такая же идея, почти персонаж за персонажем.
А вот демонстрационная ручка со всем кодом для каждого примера. Они пронумерованы, поэтому, чтобы проверить еще один, закомментируйте тот, который не закомментирован, и раскомментируйте другой пример:
https://codepen.io/chriscoyier/pen/xxXeXPN