JS30. Задание 5 Flex Panel Gallery


Демо https://js3005.github.io/
Код https://github.com/js3005/js3005.github.io

Прекрасная галерея на js, которая требует всего несколько строчек несложного кода.
Основная магия здесь спрятана в css, зато js проще некуда.

Итак, у нас есть несколько div с классом panel, которые и являются слайдами нашего слайдшоу

Шаг первый - находим их все

var panels = document.querySelectorAll(".panel");

Напоминаю в первую очередь себе, что метод querySelectorAll возвращает что-то вроде массива, а на самом деле объект NodeList, содержащий все найденные элементы, к которым можно обратиться через перебор или методом forEach

Создаём функцию toggleOpen
Метод  classList.toggle позволяет добавить класс open элементу, у которого этого класса нет, и убрать класс у элемента, у которого такой класс есть. Очень удобно

function toggleOpen() {
    this.classList.toggle("open");
}


Сам класс open прописан, разумеется, в css, Тоже ничего сложного

.panel.open {
    flex: 5;

}

А было

.panel {
    flex: 1;

}

flex: 1; означает, что все панели занимают одинаковое количество места на экране

flex: 5; что панель будет занимать столько же места, как и пять обычных панелей, то есть, её размер увеличится в 5 раз по сравнению с остальными.

Вот как это свойство описывает MDN

flex-grow определяет, какую часть свободного пространства может занять контейнер, в соотношении с другими контейнерами. 

Создаём слушатель, который при клику по панели будет добавлять ей класс open

panels.forEach(panel => panel.addEventListener("click", toggleOpen));

Чуть-чуть дополним код, чтобы событие срабатывало и по клику и при наведении

Эмм.. добавила
Вообще, движение мышки это "mousmove", а нахождение мышки над объектом - "mouseover".  Следовательно, код будет примерно таким:

panels.forEach(panel => panel.addEventListener("mouseover", toggleOpen));

Но ни тот, ни другой вариант не порадовал. Так что останемся с тем, что было изначально.

О, а вот это изменение мне нравится

function toggleOpen() {
    panels.forEach(panel => panel.classList.remove("open"));
    this.classList.add("open");
}


Если в оригинальном варианте нужно было сделать два клика по панели: первый, чтобы открыть её, второй, чтобы закрыть, то сейчас каждый клик открывает одну панель и закрывает остальные. По-моему, супер. 

Ещё один слушатель запускает функцию toggleActive в ответ на событие
transitionend, которое показывает, что  CSS transition закончил свое выполнение

panels.forEach(panel => panel.addEventListener("transitionend", toggleActive));

Это, в свою очередь, ответ на css свойство

transition: transform 0.5s;

Ну вот, когда трансформация закончилась и панель открылась полностью, запускается функция toggleActive

function toggleActive(e) {
    if (e.propertyName.includes("flex")) {
        this.classList.toggle("open-active");
    }
}


Она добавляет к панели класс  open-active, который запускает трансформацию и надпись на панели появляется полностью, слова всплывают снизу и сверху.
В js интересно условие 

if (e.propertyName.includes("flex"))

Свойство propertyName связано с наличием в стилях css-анимации. То есть мы проверяем, что трансформация флекс-элемента произошла.
Можно было бы написать проще

if (e.propertyName == ("flex-grow")), но Safari называет событие просто flex, из-за этого приходится использовать конструкцию includes("flex")

Ну и, конечно, можно было бы вообще забыть об этих сложностях с отслеживанием трансформаций и добавлять панели два класса open-active и open-active последовательно, но тогда код был бы слишком простым  уместился бы в 6 строк вместо нынешних 15.