От автора: модальное окно – небольшой блок, выпрыгивающий с каким-либо важным сообщением. К какой сложности можно отнести такие конструкции? На мой взгляд, это средняя сложность. Для правильной работы таких окон нужно учесть парочку вещей и трюков. Давайте посчитаем их.
Где в DOM находятся модальные окна?
Я обычно размещаю HTML код модальных окон перед закрывающим тегом body.
1 2 3 |
<div class="modal" id="modal"></div> </body> </html> |
Делаю я это в основном по причинам стилизации. Позиционировать модальные окна относительно тега body, который покрывает всю страницу, намного легче, в отличие от непонятного набора родительских элементов, у каждого из которых может быть свой контекст позиционирования.
Как это влияет на скрин ридеры? Я не эксперт по доступности, но слышал, что модальные окна довольно сложная штука. Роб Додсон:
Кто пытался хоть раз сделать модальные окна доступным, знают, что, несмотря на свой безобидный вид, модальные окна – это как битва с боссом в конце игры под названием веб-доступность. Они проглотят вас и выплюнут останки. К примеру, правильное модальное окно должно обладать следующими особенностями:
фокус клавиатуры должен переместиться на модальное окно, а при его закрытии должен вернуться к предыдущему activeElement;
фокус клавиатуры должен быть заблокирован на модальном окне, чтобы пользователь случайно при помощи клавиши Tab не вышел за пределы окна;
скрин ридеры также должны быть заблокированы на модальном окне, чтобы случайно не выйти из него.
Роб создал супер доступное демо с модальным окном. Также я видел недавний пример Ноя Блона и Николаса Хоффмана. Также можно посмотреть ARIA Live Regions. Если вы обрабатываете фокус вручную, нижняя часть страницы самый подходящий вариант размещения модального окна.
Центрирование
Сейчас я покажу вам один из любимых трюков. Способ вертикального и горизонтального центрирования без значений ширины и высоты:
1 2 3 4 5 6 |
.modal { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); } |
Отлично подходит для модальных окон, которые обычно расположены точно по центру экрана и могут быть разной ширины. Высота окна меняется в зависимости от контента.
Если вы точно знаете ширину и высоту окна, можно воспользоваться другими методами. Я взял этот метод, потому что есть шанс, что текст будет слегка размытым и трансформируется. Если вы не понимаете, о чем я говорю, поищите другие методы в руководстве по центрированию (например, отрицательные margin’ы).
Фиксированное расположение?
Обратите внимание, мы использовали position: fixed;. Суть в том, что так пользователь может прокрутить страницу, вызвать модальное окно, и оно будет точно так же центрировано, как если бы он сразу его вызвал.
На мой взгляд, фиксированное позиционирование сейчас безопасно использовать, даже на мобильных устройствах. Однако если вы точно знаете, что у вас довольно большая аудитория с очень старыми мобильными устройствами, с фиксированным расположением могут возникнуть проблемы. В таком случае возьмите position: absolute и принудительно прокручивайте страницу вверх. Или что-либо еще, не знаю, тестируйте другие способы.
Работа с шириной
На больших экранах обычное модальное окно не просто центрировано, оно также имеет ограниченную ширину (как на скриншоте выше).
1 2 3 4 |
.modal { /* уже пройденные стили */ width: 600px; } |
Проблемное место. На больших экранах у нас все хорошо, но есть множество экранов, которые в ширину даже не 600px.
Легко исправить с помощью max-width:
1 2 3 4 5 |
.modal { /* уже пройденные стили */ width: 600px; max-width: 100%; } |
Работа с высотой
С высотой проблем еще больше. Контент ведь может изменяться! Метод центрирования при помощи трансформаций так и норовит отрезать верхушку окна, а полосы прокрутки у нас нет:
Нас опять спасет максимальное значение:
1 2 3 4 5 |
.modal { /* уже пройденные стили */ height: 400px; max-height: 100%; } |
Работа с перекрытием
Мы пытаемся решить вопрос с высотой модального окна. Теперь нам нужно учесть еще и перекрытие. Заманчиво было бы использовать свойство overflow прямо на элементе .modal, но тут возникают две проблемы:
у нас могут быть элементы, которые не нужно прокручивать;
перекрытие отрежет тень box-shadow, которая нам может понадобиться.
Я бы предложил ввести внутренний контейнер:
1 2 3 4 5 6 |
<div class="modal" id="modal"> <!-- то, что не нужно прокручивать --> <div class="modal-guts"> <!-- то, что нужно прокручивать --> </div> </div> |
Чтобы область .modal-guts прокручивалась, ей необходимо указать высоту. Тут много способов. Один из способов – спозиционировать внутренний контейнер так, чтобы он перекрывал все модальное окно, а потом добавить перекрытие:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
.modal-guts { /* уже пройденные стили */ /* перекрываем модальное окно */ position: absolute; top: 0; left: 0; width: 100%; height: 100%; /* добавляем отступов, если нужно */ padding: 20px 50px 20px 20px; /* добавляем полосу прокрутки */ overflow: auto; } |
Кнопки
Модальные окна должны принуждать пользователя к какому-либо действию, пока не случилось что-то еще. Если вам не нужно принуждать пользователя к действию, рассмотрите другой UI элемент. Из модального окна должен быть выход. Для модального окна характерны различные кнопки и переключатели (к примеру, «Удалить»/«Отменить»), а также кнопка закрытия. Добавим в наше окно кнопку закрытия.
Было бы умно, расположить кнопку закрытия так, чтобы она постоянно была на виду. Чтобы у пользователя не возникло ситуации, когда он не знает, как закрыть окно. Вот для этого мы и оставили область без прокрутки.
Стили добавите сами 🙂
Работа с наложением
Модальные окна зачастую появляются на полноэкранном перекрывающем фоне. Полезная вещь по ряду причин:
перекрывающий слой может затемнять страницу, усиливая тем самым основную цель модального окна;
такой слой может спасти от взаимодействия с тем, что находится вне модального окна;
перекрывающий слой можно использовать в качестве большой кнопки закрытия, отмены.
Типичный код:
1 2 3 4 5 |
<div class="modal" id="modal"> <!-- модальное окно --> </div> <div class="modal-overlay" id="modal-overlay"> </div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
.modal { /* уже пройденные стили */ z-index: 1010; } .modal-overlay { /* рекомендация: Не зацикливайтесь на значении "1000", у вас должна быть задокументированная система для свойства z-index, которую необходимо соблюдать. Данное значение должно быть довольно большим в вашей системе. */ z-index: 1000; position: fixed; top: 0; left: 0; width: 100%; height: 100%; } |
Закрытие по классу, а не открытие
Крайне заманчиво по умолчанию спрятать класс .modal при помощи свойства display: none;. Затем по открытию окна добавлять класс .modal.open { display: block; }.
Видите свойство display: block;? Вот тут и проблема. Свойство display: none; очень полезно, так как оно прячет окно не только визуально, но и от вспомогательных технологий для людей с ограниченными возможностями. Легче применять свойство поверх существующего значения свойства display, а не переписывать значение наугад. Модальное окно .modal может использовать display: flex;, display: grid; или что-либо другое. Различные модальные окна могут использовать любые значения и не бояться сбросить его на display: block;.
1 2 3 4 5 6 7 |
.modal { /* к примеру... */ display: flex; } .modal.closed { display: none; } |
Переключатель открытия и закрытия
Самый базовый способ для открытия и закрытия окна:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
var modal = document.querySelector("#modal"); var modalOverlay = document.querySelector("#modal-overlay"); var closeButton = document.querySelector("#close-button"); var openButton = document.querySelector("#open-button"); closeButton.addEventListener("click", function() { modal.classList.toggle("closed"); modalOverlay.classList.toggle("closed"); }); openButton.addEventListener("click", function() { modal.classList.toggle("closed"); modalOverlay.classList.toggle("closed"); }); |
Окно пока что еще недоступно для скрин ридеров. Вспомните, о чем мы говорили выше. По ссылке вы найдете демо, в котором фокус переходит на модальное окно, блокируется на нем и возвращается на тот элемент, с которого пришел.
Автор: Chris Coyier
Источник: //css-tricks.com/
Редакция: Команда webformyself.