Цель инициативы Open UI — упростить разработчикам создание превосходного пользовательского опыта. Для этого мы пытаемся решить наиболее проблемные задачи, с которыми сталкиваются разработчики. Мы можем сделать это, предоставляя более совершенные встроенные API и компоненты платформы.
Одной из таких проблемных областей являются всплывающие окна, которые в Open UI описываются как "Popopers".
Всплывающие окна (popovers) уже давно имеют довольно противоречивую репутацию. Отчасти это связано со способом их создания и использования. Создать их качественно непросто, но они могут принести большую пользу, направляя пользователей к определенным элементам или информируя их о контенте на вашем сайте — особенно если используются со вкусом.
При создании всплывающих булочек часто возникают две основные проблемы:
- Как убедиться, что оно будет размещено над остальным контентом в подходящем месте.
- Как сделать его доступным (удобным для клавиатуры, с возможностью фокусировки и т.д.).
Встроенный API для всплывающих окон преследует множество целей , объединенных одной общей задачей — упростить разработчикам создание подобного шаблона. К числу наиболее важных относятся:
- Обеспечьте простое отображение элемента и его потомков поверх остальной части документа.
- Сделайте его доступным.
- Для большинства распространенных функций (закрытие подсветки, синглтон, наложение элементов и т. д.) 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();
При разработке приложения для пользователей Safari версий ниже 15.4 рекомендуется учитывать некоторые аспекты доступности. Например, рекомендуется использовать a11y-dialog.
Вы также можете использовать одну из многочисленных библиотек для создания всплывающих окон, предупреждений или подсказок. Многие из них работают аналогичным образом.
- Добавьте в тело документа какой-нибудь контейнер для отображения всплывающих окон.
- Оформите его так, чтобы он выделялся на фоне всего остального.
- Создайте элемент и добавьте его в контейнер, чтобы отобразить всплывающее окно.
- Скройте его, удалив элемент всплывающего окна из DOM.
Это требует дополнительной зависимости и принятия большего количества решений от разработчиков. Также это требует поиска решения, которое бы предоставляло все необходимое. API Popover призван охватить множество сценариев, включая всплывающие подсказки. Цель состоит в том, чтобы охватить все эти распространенные сценарии, избавляя разработчиков от необходимости принимать еще одно решение, чтобы они могли сосредоточиться на создании своего пользовательского опыта.
Ваше первое всплывающее окно
Это всё, что вам нужно.
<div id="my-first-popover" popover>Popover Content!</div>
<button popovertoggletarget="my-first-popover">Toggle Popover</button>
Но что же здесь происходит?
- Вам не нужно помещать элемент всплывающего окна в контейнер или что-либо подобное — он скрыт по умолчанию.
- Для его отображения не требуется писать JavaScript-код. Это обрабатывается атрибутом
popovertoggletarget. - Когда всплывающее окно появляется, оно перемещается на верхний слой. Это означает, что оно располагается выше
documentв области просмотра. Вам не нужно управлятьz-indexили беспокоиться о местоположении всплывающего окна в DOM. Оно может быть глубоко вложено в DOM, имея отсеченных предков. Вы также можете увидеть, какие элементы в данный момент находятся на верхнем слое, с помощью инструментов разработчика. Подробнее о верхнем слое см. в этой статье .

- Функция "Легкое закрытие" доступна по умолчанию. Это означает, что вы можете закрыть всплывающее окно с помощью сигнала закрытия, например, щелкнув за пределами окна, перейдя к другому элементу с помощью клавиатуры или нажав клавишу Esc . Откройте его снова и попробуйте!
Что ещё дают всплывающие окна? Давайте рассмотрим пример подробнее. Рассмотрим эту демонстрацию с некоторым контентом на странице.
Эта плавающая кнопка действия имеет фиксированное положение с высоким 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 Grid или Flexbox, возможно, будет разумно обернуть его в элемент. В противном случае вам потребуется объявить отдельное правило, которое изменяет 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 для отображения всплывающего окна. Для его закрытия мы используем "Light dismiss". Но вы также можете использовать атрибуты 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-свойства определяют временной интервал для наведения курсора на элемент, на который реагирует всплывающее окно. В экспериментах по умолчанию всплывающее окно отображалось через явно заданные 0.5s с помощью свойства :hover . Затем для его закрытия требовалось легкое закрытие или открытие другого всплывающего окна (подробнее об этом позже). Это было связано с тем, что длительность скрытия всплывающего окна была установлена на 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. Но как насчет поведения всплывающих окон в целом? Что, если вам не нужен режим "Легкое закрытие"? Или вы хотите применить шаблон "синглтон" к вашим всплывающим окнам?
API для всплывающих окон позволяет указывать три типа всплывающих окон, различающихся по своему поведению.
[popover=auto]/[popover] :
- Поддержка вложенности. Это означает не только вложенность в DOM. Определение родительского всплывающего окна — это такое окно, которое:
- Связаны по позиции DOM (дочерний элемент).
- Связанные между собой посредством активации атрибутов дочерних элементов, таких как
popovertoggletarget,popovershowtargetи так далее. - связь осуществляется посредством атрибута
anchor(в разработке находится API для привязки CSS).
- Световой сигнал выключен.
- Открытие всплывающих окон закрывает другие всплывающие окна, которые не являются родительскими . Поэкспериментируйте с приведенной ниже демонстрацией, которая показывает, как работает вложенность с родительскими всплывающими окнами. Посмотрите, как изменение некоторых экземпляров
popoverhidetarget/popovershowtargetнаpopovertoggletargetменяет ситуацию. - При удалении одного элемента из стека удаляются все элементы, но удаление одного элемента из стека удаляет только те, которые находятся выше него в стеке.
[popover=manual] :
- Не закрывает другие всплывающие окна.
- Запрещено выключение света.
- Требуется явное закрытие с помощью элемента-триггера или JavaScript.
API JavaScript
Если вам нужен больший контроль над всплывающими окнами, вы можете использовать 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 для всплывающих окон приоритетом является доступность. Сопоставления доступности связывают всплывающее окно с элементом-триггером по мере необходимости. Это означает, что вам не нужно объявлять атрибуты aria-* , такие как aria-haspopup , если вы используете один из атрибутов-триггеров, например, popovertoggletarget .
Для управления фокусом можно использовать атрибут autofocus, чтобы переместить фокус на элемент внутри всплывающего окна. Это аналогично работе с диалоговым окном, но разница проявляется при возврате фокуса, и это связано с эффектом легкого закрытия. В большинстве случаев закрытие всплывающего окна возвращает фокус на ранее сфокусированный элемент. Но при легком закрытии фокус перемещается на элемент, на который был нажат клик, если ему удается получить фокус. Подробнее об управлении фокусом см. в разделе пояснения.
Для просмотра этой демонстрации вам потребуется открыть « полноэкранную версию ».
В этой демонстрации выделенный элемент обводится зелёной рамкой. Попробуйте перемещаться по интерфейсу с помощью клавиши Tab на клавиатуре. Обратите внимание, куда возвращается фокус при закрытии всплывающего окна. Вы также можете заметить, что при перемещении с помощью клавиши Tab всплывающее окно закрывается. Это предусмотрено разработчиками. Хотя всплывающие окна имеют функцию управления фокусом, они не перехватывают фокус. А навигация с помощью клавиатуры определяет сигнал закрытия, когда фокус перемещается за пределы всплывающего окна.
Система закрепления (в стадии разработки)
Что касается всплывающих подсказок, то сложной задачей является привязка элемента к его триггеру. Например, если всплывающая подсказка должна отображаться над триггером, но документ прокручивается, эта подсказка может быть обрезана областью просмотра. Существуют современные решения на JavaScript, такие как " Floating UI ". Они автоматически перепозиционируют всплывающую подсказку, предотвращая это, и полагаются на желаемый порядок позиционирования.
Но мы хотим, чтобы вы могли определять это с помощью своих стилей. Для решения этой задачи разрабатывается дополнительный API, работающий параллельно с 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 используйте команду
showPopover. - После истечения таймаута
2000msскройте его с помощьюhidePopover.
Тосты
В этой демонстрации верхний слой используется для отображения уведомлений в виде всплывающих сообщений (toast).
- В качестве контейнера используется всплывающее окно с текстом «Ввод
manual. - Новые уведомления добавляются во всплывающее окно, и это всплывающее окно отображается.
- Они удаляются с помощью API веб-анимации по клику и исчезают из DOM.
- Если тостов нет, значит, поп-овер спрятан.
Вложенное меню
В этой демонстрации показано, как может работать вложенное навигационное меню.
- Используйте
[popover=auto]так как это позволяет создавать вложенные всплывающие окна. - Для навигации с помощью клавиатуры используйте
autofocusна первой ссылке каждого выпадающего списка. - Это идеальный кандидат для использования API CSS Anchoring. Но для этой демонстрации вы можете использовать небольшой фрагмент 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видео и отображает его во всплывающем окне. - Событие
popoverhideв popovers приостанавливает воспроизведение видео.
В стиле вики-поповеров
В этом примере показано, как можно создавать всплывающие подсказки, содержащие медиафайлы.
- Использует
[popover=auto]. Отображение одного элемента скрывает остальные, поскольку они не являются исходными. - Отображается на
pointerenterс помощью JavaScript. - Ещё один идеальный кандидат для использования API CSS Anchoring.
Навигационная панель
В этом примере создается выдвижная панель навигации с помощью всплывающего окна.
- Использует
[popover=auto]для закрытия подсветки. - Использует
autofocusдля фокусировки на первом элементе навигации.
Управление фоном
В этом примере показано, как можно управлять фоном для нескольких всплывающих окон, если вам нужно, чтобы отображался только один ::backdrop .
- Используйте JavaScript для ведения списка видимых всплывающих окон.
- Присвойте имя класса самому нижнему всплывающему окну в верхнем слое.
Всплывающее окно с пользовательским курсором
В этом примере показано, как использовать popover для перемещения canvas на верхний слой и отображения пользовательского курсора.
- Поднимите
canvasна верхний слой с помощьюshowPopoverи[popover=manual]. - Когда открываются другие всплывающие окна, скройте и покажите всплывающее окно
canvas, чтобы убедиться, что оно находится сверху.
Всплывающее окно с инструкциями
В этом примере показано, как можно использовать всплывающее окно в качестве интерактивного меню.
- Отображайте всплывающее окно по умолчанию, переопределяя параметр
display. - Информационный лист открывается при нажатии на триггер всплывающего окна.
- Когда всплывающее окно отображается, оно перемещается на верхний слой и становится видимым.
- Для возврата товара можно использовать функцию «Легкий отказ».
Всплывающее окно активировано с помощью клавиатуры
В этом примере показано, как можно использовать всплывающие окна для создания пользовательского интерфейса в стиле палитры команд.
- Нажмите cmd + j, чтобы отобразить всплывающее окно.
-
inputосуществляется с помощьюautofocus. - Выпадающий список представляет собой второе
popoverрасположенное под основным полем ввода. - Функция «Закрыть при нажатии» закрывает палитру, если выпадающее меню отсутствует.
- Ещё один кандидат на роль API для привязки
Временная всплывающая окно
В этой демонстрации показано всплывающее окно, появляющееся через четыре секунды после начала действия приложения. Это часто используемый в приложениях шаблон пользовательского интерфейса, хранящих конфиденциальную информацию о пользователе, для отображения модального окна выхода из системы.
- Используйте JavaScript для отображения всплывающего окна после периода бездействия.
- При появлении всплывающего окна сбросьте таймер.
Заставка
Как и в предыдущей демонстрации, вы можете добавить немного игривости на свой сайт и установить заставку.
- Используйте JavaScript для отображения всплывающего окна после периода бездействия.
- Функция «Выключить свет» позволяет скрыть подсветку и сбросить таймер.
Карет следует
В этом примере показано, как можно сделать так, чтобы всплывающее окно следовало за курсором поля ввода.
- Всплывающее окно отображается в зависимости от выбора, нажатия клавиши или ввода специального символа.
- Используйте JavaScript для обновления положения всплывающего окна с помощью пользовательских свойств, ограниченных областью видимости.
- Такой подход потребует тщательного обдумывания отображаемого контента и обеспечения его доступности.
- Это часто встречается в интерфейсах текстовых редакторов и приложениях, где можно добавлять теги.
Меню плавающих кнопок действий
В этом примере показано, как можно использовать всплывающее окно для реализации плавающего меню с кнопками действий без использования JavaScript.
- Создайте всплывающее окно, отображаемое
manualс помощью методаshowPopover. Это основная кнопка. - Меню представляет собой еще одно всплывающее окно, которое является целью основной кнопки.
- Меню открывается с помощью
popovertoggletarget. - Используйте
autofocus, чтобы сфокусироваться на первом отображаемом пункте меню. - Функция «Закрыть при нажатии» закрывает меню.
- В этом варианте иконки используется функция
:has(). Подробнее о функции:has()можно прочитать в этой статье .
Вот и все!
Итак, это было введение в всплывающие окна (popover), которые появятся в будущем в рамках инициативы Open UI. При разумном использовании они станут фантастическим дополнением к веб-платформе.
Обязательно ознакомьтесь с Open UI . Всплывающее окно с пояснениями постоянно обновляется по мере развития API. А вот и вся коллекция демонстрационных примеров.
Спасибо, что заглянули!