Привет, Хабр! Представляю вашему вниманию перевод статьи "Meet the New Dialog Element" автора Keith J. Grant.

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

Я встроил полноценное демо в конец статьи, но если вы захотите взглянуть на него во время чтения, вы можете найти его здесь.

Вот пример базовой разметки для окна диалога:

<dialog open>
  Native dialog box!
</dialog>

Атрибут open означает, что диалог виден. Без этого атрибута диалог будет скрыт до тех пор, пока вы не используете JavaScript, чтобы он стал видимым. Без всякой стилизации диалог выглядит следующим образом:



На странице он спозиционирован абсолютно, так что он появится впереди всего контента, как можно ожидать, и он горизонтально центрирован. По умолчанию, его ширина равна ширине контента внутри.

Базовые операции


В JavaScript есть несколько методов и свойств для облегчения работы с элементом dialog. showModal() и close() — два метода, которые, возможно, понадобятся вам больше всего.

const modal = document.querySelector('dialog');

// показывает диалог (добавляет атрибут "open")
modal.showModal();

// скрывает диалог (удаляет атрибут "open")
modal.close();

Когда вы используете showModal() для открытия диалога, на страницу добавляется фон, блокирующий взаимодействие пользователя с контентом вне модального окна. По умолчанию фон полностью прозрачный, но вы можете сделать его видимым с помощью CSS (подробнее об этом ниже).

Нажатие Esc закроет диалог, а также вы можете создать кнопку закрытия с вызовом метода close().

Есть еще третий метод, show(), который также показывает модальное окно, но без сопутствующего фона. Пользователь сможет взаимодействовать с элементами за пределами диалога.

Поддержка браузерами и полифилл


На данный момент dialog поддерживается только в Chrome. Firefox предоставляет базовую стилизацию, однако для применения JavaScript API пользователь должен явно включить эту функцию. Я думаю, в скором времени Firefox включат его по умолчанию.

Благо, у нас есть полифилл, который поддерживает и поведение JavaScript, и стилизацию по умолчанию. Для его использования установите dialog-polyfill в npm, или используйте старый добрый тег script. Полифилл работает в IE9 и выше.

При использовании полифилла, каждый диалог на странице должен быть инициализирован:

dialogPolyfill.registerDialog(modal);

Этот прием не заменит нативное поведение диалога для браузеров, которые поддерживают его.

Стилизация


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

dialog {
  padding: 0;
  border: 0;
  border-radius: 0.6rem;
  box-shadow: 0 0 1em black;
}

dialog::backdrop {
  /* сделает фон черным полупрозрачным */
  background-color: rgba(0, 0, 0, 0.4);
}

Для старых браузеров, использующих полифилл, этот псевдоэлемент не сработает. В этом случае полифилл добавляет элемент .backdrop, который следует прямо за диалогом. Вы можете стилизовать его с помощью CSS как-то так:

dialog + .backdrop {
  background-color: rgba(0, 0, 0, 0.4);
}

Добавим немного больше разметки для стилизации. Общепринятый подход: разбить диалоговое окно на заголовок, тело и подвал:

<dialog id="demo-modal">
  <h3 class="modal-header">A native modal dialog box</h3>
  <div class="modal-body">
    <p>Finally, HTML has a native dialog box element! This is fantastic.</p>
    <p>And a polyfill makes this usable today.</p>
  </div>
  <footer class="modal-footer">
    <button id="close" type="button">close</button>
  </footer>
</dialog>

Добавив немного CSS, вы можете сделать так, чтобы модальное окно выглядело точно так, как вам нужно:



Больше контроля


Частенько мы хотим немного обратного взаимодействия с пользователем через диалоговое окно. Когда диалог закрывается, вы можете передать строковое значение в метод close(). Это значение присваивается свойству returnValue DOM-элемента dialog, так что вы можете прочесть его позже:

modal.close('Accepted');

console.log(modal.returnValue); // выведет `Accepted`

Также есть другие события, к которым вы можете подписаться. Два очень полезных: close (срабатывает при закрытии окна) и cancel (срабатывает при нажатии Esc для закрытия окна).

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

modal.addEventListener('click', (event) => {
  if (event.target === modal) {
    modal.close('cancelled');
  }
});

Это не идеально, но работает. Пожалуйста, дайте мне знать, если найдете лучший способ отслеживания кликов по фону.

Рабочее демо


Я много поработал над демо ниже. Поиграйтесь с ним и увидите, что еще можно делать с элементом dialog. Демо включает полифилл, так что оно будет работать в большинстве браузеров.

Ссылка на демо.

Комментарии (29)


  1. Varim
    07.04.2018 18:03
    +8

    Есть еще третий метод, show(), который также показывает модальное окно, но без сопутствующего фона. Пользователь сможет взаимодействовать с элементами за пределами диалога.
    Значит окно не модальное, по определению.


    1. sshikov
      07.04.2018 19:29
      +1

      По-моему, в спецификации вовсе и не написано, что оно должно быть непременно только модальным.


      1. Keyten
        07.04.2018 23:28

        А в статье написано


  1. GellTorn
    07.04.2018 19:43
    -4

    Проще свое всплывающее окно написать


    1. Akdmeh
      07.04.2018 20:47
      +1

      Может и проще, но этот способ будет более семантичным — поисковики могут со временем лучше понимать, что находится внутри этого тега и соответственно индексировать его содержимое.


      1. Juralis
        07.04.2018 21:26
        -5

        Семантика — это миф. Поисковики будут индексировать лучше только то, что способствует нахождению нужной информации. Крайне сомнительно, что её будут содержать диалоговые окна. Это как правило не содержательный элемент, а интерактивный. Вы что-то делаете и вам что-то отвечают или предлагают. Поисковикам это едва ли будет интересно.


        1. Akdmeh
          07.04.2018 21:33
          +3

          Я же не сказал, что индексирование будет обязательно лучше, можно именно подсказать поисковику, что эта информация ситуативна и появляется на экране только за определенных условий.
          Второй плюс — очевидность поведения для разных разработчиков. Если я буду видеть тег dialog, то буду довольно уверен, что это именно выпадающее окно, которое работает по определенному единому интерфейсу управления через JS. А в библиотеках по выводу модальных окон никакой унификации нет — в каждой свои названия методов и параметры.
          Ну и если так подумать, то своего времени подобные высказывания были и о новых html5-тегах: зачем они, если можно использовать <div class=«article»>
          Думаю, только время покажет, нужен этот тег или нет.


          1. sshikov
            07.04.2018 21:53
            -4

            Видите ли, с момента высказываний о html5 появилась спецификация на разработку custom тэгов. И фреймворки типа полимера (только в качестве примера). И сейчас, на мой взгляд, вопрос стоит именно так — нафига добавлять в спецификацию хоть какие-то новые элементы, когда написание своего уже давно не является чем-то запредельно сложным, а разметка в итоге выглядит ровно так, как нам тут нарисовали (то есть, как угодно, так и будет выглядеть)?

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

            Что же до очевидности поведения… ну практика показывает, что это совсем неоднозначная штука, и очевидным люди могут считать вещи совершенно разные. Было бы лучше, если бы поведение а) легко настраивалось и б) можно было эти настройки узнать, как заглядывая, так и не заглядывая в документацию.


        1. sshikov
          07.04.2018 21:36
          +7

          Если они не будут индексировать то что не нужно (включая, например, диалоги) — это тоже хорошо.


        1. HaJIuBauKa
          07.04.2018 22:00

          Хм… А еще в диалогах могут быть формы, и поисковикам это ой как интересно. И еще там много чего может быть.


    1. SPAHI4
      07.04.2018 21:34
      +8

      Удачи в реализации accessibility


    1. HaJIuBauKa
      07.04.2018 21:59
      +4

      Ну можно же написать свой браузер. Что же вы так сразу.


    1. Serator
      08.04.2018 14:20

      Всплывающее окно, возможно, и проще, а вот модальное нет.


  1. ivorobioff
    08.04.2018 00:37

    А где меню выпрыгивающее и прочие крутые UI штуки? А че там меню, может к моменту выхода html 6 у нас будет весь набор Twitter Bootstrap-a из коробки :)


  1. ExplosiveZ
    08.04.2018 02:46
    -2

    На данный момент dialog поддерживается только в Chrome. Firefox предоставляет базовую стилизацию, однако для применения JavaScript API пользователь должен явно включить эту функцию. Я думаю, в скором времени Firefox включат его по умолчанию.

    Как связана стилизация с JS API и какую функцию нужно включить вручную?
    Firefox 59, демка работает.


    1. NelGarbuzova Автор
      08.04.2018 09:01

      Поддержка JS API в Firefox доступна с 53 версии, но только при включенном флаге dom.dialog_element.enabled = true.


    1. NelGarbuzova Автор
      08.04.2018 09:10
      +1

      Также, читайте внимательнее

      Демо включает полифилл, так что оно будет работать в большинстве браузеров.


  1. bro-dev
    08.04.2018 05:03
    -2

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


    1. flatscode
      08.04.2018 07:28

      Похоже на «имитацию бурной деятельности».


    1. VolCh
      08.04.2018 09:03
      +1

      Стандартизация.


    1. Serator
      08.04.2018 14:24

      Модальное окно без этого элемента стандартными средствами сделать фактически невозможно, так как модальность подразумевает недоступность элементов под модальным окном для устройств ввода, что крайне сложно / невозможно имитировать.


      1. evgkul
        08.04.2018 20:39

        А трюк с :checked ~ чем не подходит?


        1. sgrogov
          09.04.2018 15:58

          Думаю Serator говорил не о том, что невозможно сделать модальное окно работающее без js, а о том, что невозможно гарантировать, что ни один элемент не вылезет вперёд модального окна


  1. dopusteam
    08.04.2018 08:04
    +1

    dialog. showModal() и close()


    Интересно, чем обусловлено такая неконсистентность в названиях?

    Почему бы не сделать showModal и hideModal? Или openModal и closeModal?
    Мало того что глаголы не совпадающие, так ещё и существительное опциональное


    1. VolCh
      08.04.2018 09:09
      +2

      Modal — модификатор. show и showModal. Закрытие одинаково.

      close относится к атрибуту open.


  1. Fen1kz
    08.04.2018 15:26

    Предлагаю добавить опрос «как думаете, когда эту штуку можно будет использовать в рабочих проектах.» и варианты — 2 года, 4 года, 8 лет, 16 и 32


    1. Ivanq
      08.04.2018 16:14

      2 4 8 16 32 — это хорошо, но 0 тоже не хватает. Учитывая то, что есть полифилл, а встроенная поддержка скоро будет, удобнее использовать `dialog`, чем каждый раз писать самому или использовать библиотеку.


  1. lxsmkv
    08.04.2018 16:51

    У меня у одного такое подспудное ощущение, что модальные окна должны были умереть еще четверть века назад?

    Ведь модальное окно блокирует любое взаимодействие с приложением. И в некоторых случаях, например при сохранении может быть решает какую-то проблему. Чтобы сохраняемые данные не изменились перед сохранением. Однако, даже тут технологии ушли так далеко, что даже кнопки сохранить во некоторых приложениях не стало.

    И мне обидно за пользователей. Почему неспособность разрешить каку-то техническую проблему ложится на плечи пользователя? «Пусть сам решает».
    Хотя именно принятие решения, это самый «болезненный» для человекого мозга процесс.

    Я помню презентацию TimeMachine от Аpple и эту фразу: «We back up everything». Просто и понятно. Никаких условий о которых нужно помнить. Подключил диск и забыл. Не нужно понимать как это работает. Не нужно беспокоиться ни о чем. Бери и пользуйся. «Мы, технические специалисты, решили твою проблему для тебя, пользователь. Мы разгрузили твой мозг, чтобы ты смог использовать его для творческой работы». Компьютерные технологии должны (да обязаны даже) упрощать жизнь, а не усложнять ее.

    И еще больший вопрос у меня возникает вот из-за чего. Ребята, запомните этот момент. Изначально сомнительное решение становится частью стандарта. Стандарта. Мирового, общепризнанного стандарта. Вы все свидетели этого. Чтобы потом не спрашивать, откуда берутся все эти окаменелости. Когда-то достигается та точка где прогресс переходит в регресс.

    HTML5 был долгожданным и нужным глотком свежего воздуха.Его долго вынашивали, и не зря. Но прошло время и он стал идти на поводу у чьих-то хотелок.

    У нас на проекте я тоже наблюдал похожий сценарий. Был сделан фреймворк, он умел не все, но по ходу дела в него без разбору стали добавлять все хотелки разработчиков, («хотите? сделаем! проще добавить чем думать») и теперь страшно стало им пользоваться потому что баги, баги, баги, и нет им конца.

    Вобщем, «Давайте делать паузы в пути ...», как говорится в песне.

    использованные ссылки и материалы

  1. Klestofer
    08.04.2018 16:56

    Ещё есть такая полезная возможность: внутри диалога размещается форма c атрибутом method=«dialog». И теперь, при отправке этой формы, диалог будет автоматически закрываться, а его значение returnValue будет взято из атрибута value кнопки отправки формы. Причём в обработчике submit этой формы можно вызвать event.preventDefault()(например в случае неудачной валидации введённых в форму данных) и тогда диалог не закроется. Очень удобно.

    Подробности здесь.