Интерфейсы и frontend
January 21

Нативные модалки

Содержание

Предисловие

Пару лет назад, я писал про предложение о добавлении элемента, который стал бы нативным попапом. Судя по комментарию в топике этого предложения, идея элемента popup была перенесена в новую концепцию — Popover API. К моему удивлению, в данный момент это API поддерживается большинством современных браузеров, кроме Firefox. Подробности с примерами можно почитать, как в переводе на хабре, так и в оригинале. К своему стыду, я упустил новость о внедрении этого API в браузеры, хотя это произошло уже почти год назад. Постараюсь найти время и собрать под это дело интерактивную демку.

Немного истории

Внедрение различных новых фич в браузеры — это всегда повод для радости. В декабре я писал про свойство field-sizing (на момент публикации, в интернете об этом почти не было постов, я в числе первых, кто написал об этом на русском языке), сегодня хочу поделиться с вами новостью про добавление нового элемента <dialog>. Одним из первых про это написал англоязычный блогер Keith J. Grant, почти 6 лет назад. Ближе к концу 2018 года, на канале веб-стандарты появился пост со ссылкой на статью, в которой был приведен компонент a11y-dialog, и его адаптации для vue и react.

⚠️ Стоит оговориться, что a11y-dialog на самом деле внутри себя не использует нативный элемент, а скорее является неким фоллбэком и хорошим аналогом, максимально приближенным к идеалу.

И вот уже почти 2 года прошло с того момента, когда вышла статья на сайте webkit о внедрении элемента dialog, считают это той самой отправной точкой, после которой нет больше смысла в использовании сторонних решений для модальных и диалоговых окон.

Как пользоваться?

Я собрал небольшую демку на codepen, где используется элемент <dialog> для отрисовки модального окна. Есть минимальная стилизация. Давайте разберём эту демку и попробуем как-то её усложнить.

<style>
::backdrop {
  background: #000;
  opacity: 0.75;
}

dialog {
  border: unset;
  border-radius: 10px;
}

dialog form {
  display: flex;
  justify-content: end;
}
</style>

<button onclick="dialog.showModal()">Открыть модалку</button>

<dialog id="dialog">
  <p>Это нативное модальное окно</p>

  <form method="dialog">
    <button>ОК</button>
  </form>
</dialog>

Начнём со стилизации, псевдокласс ::backdrop позволяет отрисовать затемнение общего фона страницы, чтобы отделить модальное окно от всего остального интерфейса. Стилизация самого элемента и формы не так интересна, её можно пропустить.

Дальше по коду идёт кнопка, с обработчиком onclick, в котором обозначен вызов метода showModal() на элементе с id dialog. Внутри тела самого модального окна есть форма, которая отвечает за различные действия, которые можно совершить. По умолчанию, нажатие на кнопку будет закрывать модальное окно.

Логично, что модальные окна могут быть не только информационными, одного закрытия по кнопке маловато. Изменить это можно следующим образом:

<button onclick="dialog.showModal()">Открыть модалку</button>

<dialog id="dialog">
  <p>Хотите продолжить?</p>

  <form method="dialog">   
    <button type="submit" value="no">Нет</button>
    <button type="submit" value="yes">Да</button>
  </form>
</dialog>

<script>
  dialog.addEventListener('close', (event) => {
    if (dialog.returnValue === 'yes') { /* ... */ }
  });
</script>

Таким образом, мы получаем полнофункциональное модальное окно, которое можно и нужно использовать вместо самописных велосипедов. Доступность у такого элемента точно будет выше, чем у написанного руками. Бывают случаи, когда фремйворки соблюдают рекомендации WAI ARIA, как например, Bootstrap. Но если есть возможность заменить ненативный элемент нативным, это всегда большой плюс.

В чём отличие модального окна от диалогового?

Главное отличие заключается в том, что модальное окно является блокирующим элементом (то есть, позволяет взаимодействовать только со своим контентом, в то время, как основной контент страницы остаётся заблокированным). Диалоговое окно, в свою очередь, не является блокирующим элементом. Если хотите более подробно почитать про разницу диалоговых и модальных окон, то можете обратить внимание на эту статью.

Это заметно и по реализации элемента <dialog>. В качестве объекта DOM, у этого элемента есть два метода: show() и showModal(). Метод show() не добавляет псевдокласс ::backdrop, в то время, как метод showModal() делает это.

Из подобной разницы в поведении можно сделать вывод, что элемент <dialog> подходит не только для создания нативных модальных окон, но и для создания уведомлений (плашка уведомления, по сути, представляет из себя диалоговое окно).

Заключение

Несмотря на то, что в начале своей обзорной статьи я немного слукавил, назвав элемент <dialog> новым, его использование не слишком широко распространено на данный момент времени. Это обусловлено использованием UI-фреймворков при разработке в абсолютном большинстве случаев. Не могу сказать, что ситуация как-то сильно изменится, вплоть до того момента, пока тот же Bootstrap не заменит свою собственную реализацию модальных окон на элемент <dialog>.