Всплывающие окна: они возрождаются!

Цель инициативы Open UI — облегчить разработчикам создание удобного пользовательского опыта. Для этого мы пытаемся решить наиболее проблемные шаблоны, с которыми сталкиваются разработчики. Мы можем сделать это, предоставив улучшенные встроенные в платформу API и компоненты.

Одной из таких проблем являются всплывающие окна, описанные в Open UI как «Поповеры».

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

При создании всплывающих окон часто возникают две основные проблемы:

  • Как убедиться, что он размещен над остальным контентом в подходящем месте.
  • Как сделать его доступным (удобным для клавиатуры, фокусируемым и т. д.).

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

  • Упростите отображение элемента и его потомков над остальной частью документа.
  • Сделайте это доступным.
  • Не требуется JavaScript для большинства распространенных действий (легкое отклонение, одноэлементное выполнение, наложение и т. д.).

Вы можете ознакомиться с полной спецификацией всплывающих окон на сайте OpenUI .

Совместимость с браузером

Где теперь можно использовать встроенный API Popover? На момент написания статьи он поддерживается в Chrome Canary под флагом «Экспериментальные функции веб-платформы».

Чтобы включить этот флаг, откройте Chrome Canary и посетите chrome://flags . Затем включите флаг «Экспериментальные функции веб-платформы».

Также существует пробная версия Origin для разработчиков, которые хотели бы протестировать это в производственной среде.

Наконец, для API разрабатывается полифилл . Обязательно проверьте репозиторий на github.com/oddbird/popup-polyfill .

Вы можете проверить поддержку всплывающих окон с помощью:

const supported = HTMLElement.prototype.hasOwnProperty("popover");

Текущие решения

Что вы можете сделать в настоящее время, чтобы продвигать свой контент превыше всего? Если это поддерживается вашим браузером, вы можете использовать элемент HTML Dialog . Вам нужно будет использовать его в «модальной» форме. И для этого требуется использование JavaScript.

Dialog.showModal();

Есть некоторые соображения по поводу доступности. Рекомендуется использовать a11y-dialog, например, если обслуживаются пользователи Safari ниже версии 15.4.

Вы также можете использовать одну из многих библиотек на основе всплывающих окон, предупреждений или всплывающих подсказок. Многие из них, как правило, работают аналогичным образом.

  • Добавьте в тело контейнер для отображения всплывающих окон.
  • Оформите его так, чтобы он располагался выше всего остального.
  • Создайте элемент и добавьте его в контейнер, чтобы отобразить всплывающее окно.
  • Скройте его, удалив элемент popover из DOM.

Это требует дополнительной зависимости и большего количества решений для разработчиков. Также необходимо провести исследование, чтобы найти предложение, которое предоставит все, что вам нужно. API Popover предназначен для удовлетворения многих сценариев, включая всплывающие подсказки. Цель состоит в том, чтобы охватить все эти распространенные сценарии, избавив разработчиков от необходимости принимать еще одно решение, чтобы они могли сосредоточиться на создании своего опыта.

Ваше первое всплывающее окно

Это все, что вам нужно.

<div id="my-first-popover" popover>Popover Content!</div>
<button popovertoggletarget="my-first-popover">Toggle Popover</button>

Но что здесь происходит?

  • Вам не нужно помещать элемент popover в контейнер или что-то еще — по умолчанию он скрыт.
  • Вам не нужно писать какой-либо JavaScript, чтобы он появился. Это обрабатывается атрибутом popovertoggletarget .
  • Когда он появляется, он перемещается на верхний слой. Это означает, что он продвигается над document в области просмотра. Вам не нужно управлять z-index или беспокоиться о том, где находится ваш поповер в DOM. Он может быть глубоко вложен в DOM с обрезкой предков. Вы также можете увидеть, какие элементы в данный момент находятся на верхнем уровне, с помощью DevTools. Подробнее о верхнем слое читайте в этой статье .

Демонстрируется GIF-изображение поддержки верхнего уровня DevTools

  • Вы получаете «Легкое увольнение» из коробки. Под этим мы подразумеваем, что вы можете закрыть всплывающее окно с помощью сигнала закрытия, например, щелкнув за пределами всплывающего окна, перейдя к другому элементу с помощью клавиатуры или нажав клавишу Esc . Откройте его снова и попробуйте!

Что еще вы получаете с помощью popover? Давайте рассмотрим пример дальше. Рассмотрим эту демонстрацию с некоторым содержимым на странице.

Эта кнопка плавающего действия имеет фиксированное позиционирование с высоким z-index .

.fab {
  position: fixed;
  z-index: 99999;
}

Содержимое всплывающего окна вложено в DOM, но когда вы открываете всплывающее окно, оно перемещается над этим элементом с фиксированной позицией. Вам не нужно устанавливать какие-либо стили.

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

Стилизация всплывающего окна

Давайте обратим внимание на оформление поповера. По умолчанию поповер имеет фиксированную позицию и некоторые отступы. Он также имеет display: none . Вы можете переопределить это, чтобы показать всплывающее окно. Но это не продвинет его на верхний уровень.

[popover] { display: block; }

Независимо от того, как вы продвигаете свой поповер, как только вы переместите его на верхний слой, вам может потребоваться его расположение или размещение. Вы не можете выбрать верхний слой и сделать что-то вроде

:open {
  display: grid;
  place-items: center;
}

По умолчанию всплывающее окно будет располагаться в центре области просмотра с использованием margin: auto . Но в некоторых случаях вам может потребоваться четко указать позиционирование. Например:

[popover] {
  top: 50%;
  left: 50%;
  translate: -50%;
}

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

[popover]:open {
 display: flex;
}

Если вы опробовали эту демонстрацию, вы заметите, что всплывающее окно теперь появляется и исчезает. Вы можете включать и выключать всплывающие окна, используя псевдоселектор :open . Псевдоселектор :open соответствует отображаемым всплывающим окнам (и, следовательно, на верхнем уровне).

В этом примере используется пользовательское свойство для управления переходом. И вы также можете применить переход к ::backdrop поповера.

[popover] {
  --hide: 1;
  transition: transform 0.2s;
  transform: translateY(calc(var(--hide) * -100vh))
            scale(calc(1 - var(--hide)));
}

[popover]::backdrop {
  transition: opacity 0.2s;
  opacity: calc(1 - var(--hide, 1));
}


[popover]:open::backdrop  {
  --hide: 0;
}

Совет: группируйте переходы и анимацию по медиа-запросу для движения. Это также может помочь сохранить ваше время. Это связано с тем, что вы не можете использовать общие значения между popover и ::backdrop через пользовательское свойство.

@media(prefers-reduced-motion: no-preference) {
  [popover] { transition: transform 0.2s; }
  [popover]::backdrop { transition: opacity 0.2s; }
}

До этого момента вы видели использование popovertoggletarget для отображения всплывающего окна. Чтобы отклонить его, мы используем «Световое отклонение». Но вы также можете использовать атрибуты popovershowtarget и popoverhidetarget . Давайте добавим кнопку во всплывающее окно, которая скроет его, и изменим кнопку переключения на использование popovershowtarget .

<div id="code-popover" popover>
  <button popoverhidetarget="code-popover">Hide Code</button>
</div>
<button popovershowtarget="code-popover">Reveal Code</button>

Как упоминалось ранее, API Popover охватывает не только наше историческое представление о всплывающих окнах. Вы можете создавать сценарии для всех типов, таких как уведомления, меню, всплывающие подсказки и т. д.

Некоторые из этих сценариев требуют разных моделей взаимодействия. Взаимодействия, такие как наведение. Использование атрибута popoverhovertarget экспериментировало, но в настоящее время не реализовано.

<div popoverhovertarget="hover-popover">Hover for Code</div>

Идея состоит в том, что вы наводите курсор на элемент, чтобы показать цель. Это поведение можно настроить через свойства CSS. Эти свойства CSS будут определять окно времени для наведения и выключения элемента, на который реагирует всплывающее окно. Поведение по умолчанию, с которым экспериментировали, включало всплывающее окно после явного нажатия :hover в течение 0.5s . Тогда для закрытия потребуется легкое закрытие или открытие другого поповера (подробнее об этом позже). Это произошло из-за того, что продолжительность скрытия всплывающего окна была установлена ​​на Infinity .

Тем временем вы можете использовать JavaScript для заполнения этой функциональности.

let hoverTimer;
const HOVER_TRIGGERS = document.querySelectorAll("[popoverhovertarget]");
const tearDown = () => {
  if (hoverTimer) clearTimeout(hoverTimer);
};
HOVER_TRIGGERS.forEach((trigger) => {
  const popover = document.querySelector(
    `#${trigger.getAttribute("popoverhovertarget")}`
  );
  trigger.addEventListener("pointerenter", () => {
    hoverTimer = setTimeout(() => {
      if (!popover.matches(":open")) popover.showPopOver();
    }, 500);
    trigger.addEventListener("pointerleave", tearDown);
  });
});

Преимущество установки явного окна при наведении заключается в том, что оно гарантирует, что действие пользователя является преднамеренным (например, пользователь наводит указатель на цель). Мы не хотим показывать всплывающее окно, если это не является их намерением.

Попробуйте эту демонстрацию, где вы можете навести курсор на цель с окном, установленным на 0.5s .


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


Виды поповера

Мы рассмотрели поведение взаимодействия, отличное от JavaScript. А как насчет поведения поповера в целом? Что делать, если вы не хотите «Отклонить свет»? Или вы хотите применить шаблон Singleton к своим всплывающим окнам?

API Popover позволяет указать три типа всплывающих окон, которые различаются по поведению.

[popover=auto]/[popover] :

  • Поддержка вложения. Это означает не только вложенность в DOM. Определение наследственного поповера следующее:
    • связаны позицией DOM (дочерний).
    • связаны путем запуска атрибутов дочерних элементов, таких как popovertoggletarget , popovershowtarget и т. д.
    • связаны атрибутом anchor (API привязки CSS в разработке).
  • Свет отпустить.
  • Открытие отклоняет другие всплывающие окна, которые не являются предковыми всплывающими окнами . Поиграйте с приведенной ниже демонстрацией, в которой показано, как работает вложение с помощью предковых всплывающих окон. Посмотрите, как изменение некоторых экземпляров popoverhidetarget / popovershowtarget на popovertoggletarget меняет ситуацию.
  • Свет, отклоняющий один, отклоняет все, но отклонение одного в стопке отклоняет только те, которые находятся над ним в стопке.

[popover=manual] :

  • Не закрывает другие поповеры.
  • Нет света, выключи.
  • Требуется явное отклонение с помощью триггерного элемента или JavaScript.

JavaScript API

Если вам нужно больше контроля над всплывающими окнами, вы можете использовать JavaScript. Вы получаете как метод showPopover , так и hidePopover . У вас также есть события popovershow и popoverhide для прослушивания:

Показать всплывающее окно js popoverElement.showPopover() Скрыть всплывающее окно:

popoverElement.hidePopover()

Слушайте всплывающее окно:

popoverElement.addEventListener('popovershow', doSomethingWhenPopoverShows)

Прослушайте отображение всплывающего окна и отмените его отображение:

popoverElement.addEventListener('popovershow',event => {
  event.preventDefault();
  console.warn(‘We blocked a popover from being shown’);
})

Слушайте, как скрывается всплывающее окно:

popoverElement.addEventListener('popoverhide', doSomethingWhenPopoverHides)

Вы не можете отменить скрытие поповера:

popoverElement.addEventListener('popoverhide',event => {
  event.preventDefault();
  console.warn("You aren't allowed to cancel the hiding of a popover");
})

Проверьте, находится ли поповер на верхнем слое:

popoverElement.matches(':open')

Это обеспечивает дополнительную мощность для некоторых менее распространенных сценариев. Например, покажите всплывающее окно после периода бездействия.

В этой демонстрации есть всплывающие окна со звуковыми щелчками, поэтому для воспроизведения звука нам понадобится JavaScript. При нажатии мы скрываем всплывающее окно, воспроизводим звук, а затем показываем его снова.

Доступность

Доступность находится на переднем плане в API Popover. При необходимости сопоставления доступности связывают всплывающее окно с его триггерным элементом. Это означает, что вам не нужно объявлять атрибуты aria-* , такие как aria-haspopup , при условии, что вы используете один из триггерных атрибутов, например popovertoggletarget .

Для управления фокусом вы можете использовать атрибут autofocus, чтобы переместить фокус на элемент внутри всплывающего окна. Это то же самое, что и для диалога, но разница возникает при возврате фокуса из-за отклонения света. В большинстве случаев закрытие всплывающего окна возвращает фокус на ранее выделенный элемент. Но фокус перемещается на элемент, по которому щелкнули, при выключении света, если он может получить фокус. Ознакомьтесь с разделом об управлении фокусом в объяснителе.

Вам нужно будет открыть « полноэкранную версию » этой демонстрации, чтобы увидеть, как она работает.

В этой демонстрации элемент, находящийся в фокусе, имеет зеленый контур. Попробуйте перемещаться по интерфейсу с помощью клавиатуры. Обратите внимание, куда возвращается фокус, когда всплывающее окно закрывается. Вы также можете заметить, что если вы перешли на вкладку, всплывающее окно закрылось. Это задумано. Хотя поповеры имеют функцию управления фокусом, они не захватывают фокус. А навигация с помощью клавиатуры определяет сигнал закрытия, когда фокус выходит за пределы всплывающего окна.

Привязка (в разработке)

Когда дело доходит до всплывающих окон, возникает сложная задача — привязать элемент к его триггеру. Например, если всплывающая подсказка отображается над триггером, но документ прокручивается. Эта всплывающая подсказка может быть обрезана окном просмотра. В настоящее время существуют предложения JavaScript для решения этой проблемы, такие как « Плавающий пользовательский интерфейс ». Они изменят положение всплывающей подсказки, чтобы вы могли остановить это и положиться на желаемый порядок позиции.

Но мы хотим, чтобы вы могли определить это с помощью своих стилей. Для решения этой проблемы наряду с API Popover разрабатывается сопутствующий API. API « CSS Anchor Positioning » позволит вам привязывать элементы к другим элементам, и он будет делать это таким образом, чтобы перемещать элементы так, чтобы они не были обрезаны областью просмотра.

В этой демонстрации используется API привязки в его текущем состоянии. Положение лодки зависит от положения якоря в окне просмотра.

Вот фрагмент CSS, благодаря которому эта демонстрация работает. Никакого JavaScript не требуется.

.anchor {
  --anchor-name: --anchor;
}
.anchored {
  position: absolute;
  position-fallback: --compass;
}
@position-fallback --compass {
  @try {
    bottom: anchor(--anchor top);
    left: anchor(--anchor right);
  }
  @try {
    top: anchor(--anchor bottom);
    left: anchor(--anchor right);
  }
}

Вы можете проверить спецификацию здесь . Для этого API также будет полифилл.

Примеры

Теперь вы знакомы с тем, что может предложить popover и как, давайте рассмотрим несколько примеров.

Уведомления

В этой демонстрации показано уведомление «Копировать в буфер обмена».

  • Использует [popover=manual] .
  • При действии показывать всплывающее окно с помощью showPopover .
  • После тайм-аута 2000ms скройте его с помощью hidePopover .

Тосты

В этой демонстрации верхний слой используется для отображения всплывающих уведомлений.

  • В качестве контейнера выступает один поповер с указанием типа manual .
  • Новые уведомления добавляются во всплывающее окно, и всплывающее окно отображается.
  • Они удаляются с помощью API веб-анимации при нажатии и удаляются из DOM.
  • Если всплывающих уведомлений нет, всплывающее окно скрыто.

Вложенное меню

В этой демонстрации показано, как может работать вложенное меню навигации.

  • Используйте [popover=auto] , поскольку он позволяет вложенные всплывающие окна.
  • Используйте autofocus на первой ссылке каждого раскрывающегося списка для навигации с помощью клавиатуры.
  • Это идеальный кандидат на использование API привязки CSS. Но в этой демонстрации вы можете использовать небольшой объем JavaScript для обновления позиций с помощью пользовательских свойств.
const ANCHOR = (anchor, anchored) => () => {
  const { top, bottom, left, right } = anchor.getBoundingClientRect();
  anchored.style.setProperty("--top", top);
  anchored.style.setProperty("--right", right);
  anchored.style.setProperty("--bottom", bottom);
  anchored.style.setProperty("--left", left);
};

PRODUCTS_MENU.addEventListener("popovershow", ANCHOR(PRODUCT_TARGET, PRODUCTS_MENU));

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

Медиа-поповер

В этой демонстрации показано, как можно открыть медиафайлы.

  • Использует [popover=auto] для отключения света.
  • JavaScript прослушивает событие play видео и отображает его.
  • Событие popovers popoverhide приостанавливает видео.

Поповеры в стиле Wiki

В этой демонстрации показано, как можно создавать всплывающие подсказки для встроенного контента, содержащего медиафайлы.

  • Использует [popover=auto] . Показ одного скрывает другие, потому что они не являются предковыми.
  • Показано при pointerenter с помощью JavaScript.
  • Еще один идеальный кандидат на использование API привязки CSS.

В этой демонстрации создается панель навигации с помощью всплывающего окна.

  • Использует [popover=auto] для отключения света.
  • Использует autofocus для фокусировки на первом элементе навигации.

Управление фонами

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

  • Используйте JavaScript для создания списка видимых всплывающих окон.
  • Примените имя класса к самому нижнему всплывающему элементу верхнего слоя.

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

В этой демонстрации показано, как использовать popover для перемещения canvas на верхний слой и использовать его для отображения пользовательского курсора.

  • Поднимите canvas на верхний слой с помощью showPopover и [popover=manual] .
  • Когда открываются другие всплывающие окна, скройте и покажите всплывающее окно canvas , чтобы убедиться, что оно находится сверху.

Всплывающее окно с таблицей действий

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

  • Показывать всплывающее окно по умолчанию, переопределяя display .
  • Таблица действий открывается с помощью триггера всплывающего окна.
  • Когда всплывающее окно отображается, оно перемещается на верхний уровень и переводится в поле зрения.
  • Чтобы вернуть его, можно использовать легкое увольнение.

Всплывающее окно, активированное с помощью клавиатуры

В этой демонстрации показано, как можно использовать всплывающее окно для пользовательского интерфейса в стиле палитры команд.

  • Используйте cmd + j , чтобы отобразить всплывающее окно.
  • input фокусируется с помощью autofocus .
  • Поле со списком — это второе popover , расположенное под основным полем ввода.
  • Световое отклонение закрывает палитру, если раскрывающийся список отсутствует.
  • Еще один кандидат на API привязки

Временное всплывающее окно

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

  • Используйте JavaScript, чтобы отобразить всплывающее окно после периода бездействия.
  • Во всплывающем окне сбросьте таймер.

Заставка

Как и в предыдущей демонстрации, вы можете добавить на свой сайт немного фантазии и добавить заставку.

  • Используйте JavaScript, чтобы отобразить всплывающее окно после периода бездействия.
  • Свет выключите, чтобы скрыть и сбросить таймер.

Каретка следовать

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

  • Отображение всплывающего окна на основе выбора, ключевого события или ввода специального символа.
  • Используйте JavaScript, чтобы обновить положение всплывающего окна с помощью пользовательских свойств с заданной областью действия.
  • Этот шаблон потребует внимательного подхода к отображаемому контенту и доступности.
  • Это часто можно увидеть в пользовательском интерфейсе редактирования текста и приложениях, где вы можете отмечать теги.

Плавающее меню кнопок действий

В этой демонстрации показано, как можно использовать popover для реализации меню с плавающей кнопкой действий без использования JavaScript.

  • Продвигайте всплывающее окно manual типа с помощью метода showPopover . Это основная кнопка.
  • Меню — это еще один всплывающий элемент, который является целью основной кнопки.
  • Меню открывается с помощью popovertoggletarget .
  • Используйте autofocus , чтобы сфокусировать первый отображаемый пункт меню.
  • Световой индикатор закрывает меню.
  • Для поворота значка используется :has() . Подробнее о :has() вы можете прочитать в этой статье .

Вот и все!

Итак, это введение в popover, которое появится в будущем в рамках инициативы Open UI. При разумном использовании он станет фантастическим дополнением к веб-платформе.

Обязательно ознакомьтесь с Open UI . Объяснитель всплывающего окна обновляется по мере развития API. А вот и сборник всех демок.

Спасибо, что «заглянули»!


Фото Мэдисон Орен на Unsplash