Модальные окна в Alpine.js

Обычно для организации модального окна (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>

Close

Content

Этот вариант очень похож на 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>
×
Header
Content
Footer

Это типовые классы, единственное что пришлось добавить — это 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" ...
×
Header
Content
Footer

Полное описание есть в официальной документации. Конкретно здесь:

  • transition — указываем, что нужно сделать css-переход
  • in.opacity.duration.500ms — при отображении (in) использовать только прозрачность (opacity) с длительностью (duration) полсекунды (500ms)
  • out.duration.400ms — скрытие (out) сделать «стандартное» (zoom + fade) с длительностью 400 мс

Пока в 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>
×
Header
Content
Footer

Получилось довольно забавно. :-)

Правда, здесь следует отметить, что не все анимационные эффекты будут работать, поскольку многие из них требуют position: relative, а здесь это position: absolute. В таких случаях для контента может понадобиться дополнительный div-блок, где уже можно указать любые эффекты.

×
Header
Content
Footer

Итого

Очевидно, что Alpine.js прекрасно справилась с созданием модального окна. Код при этом получается совсем простым, по сути это обычная html-разметка. Можно даже сказать, что это универсальный вариант, который легко адаптируется к реальным задачам.

Комментариев: 2 RSS

1Александр30-11-2020 12:13

В 5-й версии Бутстрапа тоже отказались от зависимости Jquery.

Теперь у них будет только нативный JS.

https://v5.getbootstrap.com/docs/5.0/getting-started/contents/#precompiled-bootstrap

Оставьте комментарий!

Комментарий будет опубликован после проверки. Вы соглашаетесь с правилами сайта.

(обязательно)