Модальные окна в Alpine.js
30-11-2020Reading time ~ 6 min.Alpine.js 5399
Обычно для организации модального окна (js) используют сторонние jQuery-плагины. В общем-то это не удивительно, поскольку для этого требуется не только особая html-разметка, но и отслеживание различных событий.
На нативном JavaScript модальное окно реализуется ещё сложнее, поскольку нужно отслеживать события для открытия, закрытия окна, а также клик вне самого окна. Конечно, всё это делается в виде отдельной функции. Например так я сделал свой вариант в Berry CSS.
Кроме этого, модальное окно как правило требует css-классов, поскольку нужно особое позиционирование. Всё это приводит к тому, что в 99% случаев модальные окна создаются через дополнительные зависимости. В этом разрезе интересно будет посмотреть как Alpine.js сможет справиться с этой задачкой.
Что такое модальное окно?
Показ окна происходит по клику обычной кнопки.
Само окно — это два вложенных друг в друга div-блока. Обёртка — контейнер (или оверлей) разворачивается на всё окно браузера, имеет фиксированное положение и обычно небольшую прозрачность. Внутренний блок - это непосредственное содержимое модального окна. Как правило, оно центрируется по вертикали и горизонтали и имеет какие-то ограничения по ширине. Внутри может быть что угодно, например шапка, подвал, форма и т.д.
Закрытие окна происходит по клику на «Х», который располагается в правом верхнем углу (это типовое расположение), либо делается отдельная кнопка «Закрыть». Так же в WEB принято, что такие окна закрываются по клику вне области модального окна.
В качестве «плюшек» добавляется различная анимация — обычно это плавное открытие и закрытие окна.
Показ/скрытие блока в Alpine
Для начала рассмотрим вариант простого показа и скрытия блока.
<div x-data="{show: false}"> <button @click="show = true" class="button button1">Show content</button> <div x-show="show" x-cloak> <div @click.away="show = false"> <span @click="show = false">Close</span> Content </div> </div> </div>
Этот вариант очень похож на dropdown, который я уже показывал ранее. По клику на кнопку меняется переменная show
, которая привязана к div-блоку в директиве x-show
.
Директива @click.away
устанавливает переменную show
в false, что автоматом скрывает блок. Клик по span-элементу («Close») также закрывает окно.
По сути мы уже получили основной функционал модального окна.
HTML-разметка модального окна
Используя классы Berry CSS, создадим нужное оформление.
<div x-data="{modalShow: false}"> <button @click="modalShow = true" class="button button1">Open Modal</button> <div x-show="modalShow" class="pos-fixed pos0-l pos0-t h100 w100 z-index99 bg-op60" x-cloak> <div @click.away="modalShow = false" class="pos-absolute w100 bg-white w600px-max" style="left: 50%; top: 50%; transform: translate(-50%, -50%);"> <span @click="modalShow = false" class="pos-absolute pos10-r pos0-t t150 t-white hover-t-gray200 cursor-pointer">×</span> <div class="pad10 bg-blue600 t-white">Header</div> <div class="pad20-tb pad10-rl">Content</div> <div class="pad10 bg-gray100 t-gray600">Footer</div> </div> </div> </div>
Это типовые классы, единственное что пришлось добавить — это style для центрирования блока. В остальном всё просто, особенно если вы используете Berry CSS. Если нет, то ориентируйтесь по семантике классов, либо обратитесь к справке.
Осталось добавить анимацию. Это можно сделать двумя способами. Первый — нативная от Alpine. Вторая — css-классы от Berry либо любой другой css-библиотеки.
Анимация Alpine.js
Популярность jQuery объясняется не только тем, что она предлагает удобный и единый подход к работе с DOM, но и тем, что содержит функции для анимации. Например show()
, hide()
другие. То есть «фишка» в том, что можно обойтись без css-классов для плавного скрытия и показа блока. Те кто пытался заменить эти функции на нативный js-код и css, знают, что это требует манипулированием классами/стилями, что делает код достаточно сложным.
В Alpine подобные задачи реализуются в директиве x-show
, как дополнительные параметры. Когда мы используем простую x-show
, то это формирует стиль display: none
. Поэтому мы можем привязать к контейнеру класс анимации, например animation-fade
, чтобы анимировать показ блока.
Как правило анимации срабатывает на изменения свойства display, поэтому такой вариант и работает.
Сложность возникнет со скрытием блока — анимация здесь не сработает, поэтому блок скроется мгновенно.
Поэтому, чтобы не завязываться на классы (в данном варианте), используются дополнительные параметры для x-show
. Указываются они через точку.
<div x-show.transition.in.opacity.duration.500ms.out.duration.400ms="modalShow" ...
Полное описание есть в официальной документации. Конкретно здесь:
- transition — указываем, что нужно сделать css-переход
- in.opacity.duration.500ms — при отображении (in) использовать только прозрачность (opacity) с длительностью (duration) полсекунды (500ms)
- out.duration.400ms — скрытие (out) сделать «стандартное» (zoom + fade) с длительностью 400 мс
В статье речь идёт о Alpine 2-й версии.
Пока в Alpine два варианта эффектов: fade (opacity) и zoom (scale). С их помощью можно описать достаточно большое количество эффектов без каких-либо сторонних зависимостей.
Анимация через CSS-классы
С помощью CSS, конечно же, анимацию задавать несколько проще, да и выбор эффектов будет побольше. Для этого используется директива x-transition
, которая предназначена для указания классов, которые будут добавляться в начале и конце анимации.
Указывается это так:
<div x-show="modalShow" x-transition:enter="animation-fade" x-transition:leave="animation-fadeout" ...
Данный пример можно трактовать так: показать блок с классом «animation-fade», скрыть с «animation-fadeout». Классы могут быть любыми, например здесь я указал animation-rotatein
и animation-rotateout
<div x-data="{modalShow: false}"> <button @click="modalShow = true" class="button button1">Open Modal (rotate)</button> <div x-show="modalShow" x-transition:enter="animation-rotatein" x-transition:leave="animation-rotateout" class="pos-fixed pos0-l pos0-t h100 w100 z-index99 bg-op60" x-cloak> <div @click.away="modalShow = false" class="pos-absolute w100 bg-white w600px-max" style="left: 50%; top: 50%; transform: translate(-50%, -50%);"> <span @click="modalShow = false" class="pos-absolute pos10-r pos0-t t150 t-white hover-t-gray200 cursor-pointer">×</span> <div class="pad10 bg-blue600 t-white">Header</div> <div class="pad20-tb pad10-rl">Content</div> <div class="pad10 bg-gray100 t-gray600">Footer</div> </div> </div> </div>
Получилось довольно забавно. :-)
Правда, здесь следует отметить, что не все анимационные эффекты будут работать, поскольку многие из них требуют position: relative
, а здесь это position: absolute
. В таких случаях для контента может понадобиться дополнительный div-блок, где уже можно указать любые эффекты.
Итого
Очевидно, что Alpine.js прекрасно справилась с созданием модального окна. Код при этом получается совсем простым, по сути это обычная html-разметка. Можно даже сказать, что это универсальный вариант, который легко адаптируется к реальным задачам.
В 5-й версии Бутстрапа тоже отказались от зависимости Jquery.
Теперь у них будет только нативный JS.
https://v5.getbootstrap.com/docs/5.0/getting-started/contents/#precompiled-bootstrap
Это новости полгода. :-)