Подсказки с учетом положения

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

Запросы закрепленных контейнеров решают эту проблему, позволяя запрашивать активное состояние позиционирования элемента и соответствующим образом применять стили. Общие сведения о позиционировании закрепленных раскрывающихся списков и всплывающих меню с переворотом при столкновении с областью просмотра (без изменения стиля стрелки направления) см. в разделе Раскрывающиеся списки с учетом области просмотра.

Проблема

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

Решение: запросы к закрепленным контейнерам

By setting container-type: anchored on your positioned element, you turn it into a query container that knows about its own anchor-positioned state. Затем вы можете использовать запрос @container anchored(), чтобы обновить дочерние или псевдоэлементы.

1. Создайте подсказку и триггер

Создайте подсказку с помощью Popover API. Вызов всплывающего окна через popovertarget автоматически устанавливает неявное якорное соединение с кнопкой триггера, поэтому явные anchor-name или position-anchor не требуются.

<button popovertarget="tooltip" id="anchor" aria-describedby="tooltip">anchor</button>
<div id="tooltip" popover role="tooltip"></div>

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

@supports (anchor-name: --my-anchor) {
  [popover] {
    inset: auto;
    margin: unset;
  }
}

2. Настройте контейнер

Примените container-type: anchored к размещаемому элементу. Чтобы включить переворачивание, для этого элемента также необходимо определить position-try-fallbacks.

#tooltip {
  position: fixed;
  position-area: block-start;
  position-try-fallbacks: flip-block;

  /* Enable anchored container queries */
  container-type: anchored;
}

3. Стиль на основе резервного объявления

Используйте @container anchored(fallback: <value>), чтобы применить стили, когда активен определенный резервный вариант.

Как и все запросы контейнеров, @container может стилизовать только потомков контейнера. Обычно для создания стрелок используются псевдоэлементы ::before и ::after, которые обрабатываются как дочерние элементы и могут быть стилизованы напрямую. Однако, чтобы настроить стиль самой подсказки (как показано на шаге 4), мы добавим в нее дочерний элемент и создадим стрелку в его псевдоэлементе ::before.

<div id="tooltip" popover role="tooltip">
  <div class="tooltip-content">Tooltip</div>
</div>
.tooltip-content::before {
  /* Default "down" arrow for the 'top' position */
  content: "▼";
  position: absolute;
  inset-block-end: 0;
  inset-inline-start: 1rem;
}

/* Update to an "up" arrow when the 'flip-block' fallback (bottom) is active */
@container anchored(fallback: flip-block) {
  .tooltip-content::before {
    content: "▲";
    inset-block-start: 0;
    inset-block-end: auto;
  }
}

4. Настраиваю стиль контейнера

Если вам нужно изменить свойства самого контейнера (например, margin или background-color) при его повороте, используйте внутренний элемент-оболочку.

  1. Примените container-type: anchored к внешнему позиционированному элементу.
  2. Укажите внутренний элемент в блоке @container.
@container anchored(fallback: flip-block) {
  .tooltip-content {
    border-radius: 0 0 .5rem .5rem;
    margin-block-start: 0.25rem;
  }
}

Рекомендации

  • Используйте логические резервные варианты. Используйте ключевые слова, такие как flip-block и flip-inline в position-try-fallbacks, для более простых запросов, которые автоматически обрабатывают RTL и различные режимы записи.
  • Используйте псевдоэлементы для стрелок. Стрелки подсказок носят чисто декоративный характер и идеально подходят для ::before или ::after, стиль которых можно задать с помощью запросов к закрепленному контейнеру без дополнительной DOM.

Резервные стратегии

Позиционирование стрелки на основе примененного резервного варианта – это прогрессивное улучшение, и другого способа реагировать на резервную позицию нет. Чтобы скрыть стрелку в браузерах, которые не поддерживают запросы контейнера для позиционирования привязки, проверьте поддержку CSS с помощью @supports (container-type: anchored).

@supports (container-type: anchored) {
  .tooltip-content::before {
    content: "▼";
  }
}

Резервные варианты и поддержка Popover в браузерах

Popover API в основном представляет собой прогрессивное улучшение, но его определяющие характеристики – продвижение верхнего слоя, легкое отклонение и вызов popovertarget – не имеют эквивалента только для CSS. Для старых браузеров нужен полифил или резервный вариант, если вы не хотите его использовать.

Полифил. Чтобы обеспечить поддержку атрибута popover в старых браузерах, условно загрузите @oddbird/popover-polyfill. ОБЯЗАТЕЛЬНО. Проверьте наличие свойства popover в HTMLElement.prototype и загрузите полифил, только если встроенная поддержка отсутствует. Не загружайте его без необходимости.

С помощью сборщика или карты импорта:

// MANDATORY: Feature detect 'popover' on HTMLElement.prototype.
if (!("popover" in HTMLElement.prototype)) {
  import("@oddbird/popover-polyfill");
}

Если вы не используете сборщик, импортируйте из CDN в <script type="module">:

<script type="module">
  if (!("popover" in HTMLElement.prototype)) {
    import("https://unpkg.com/@oddbird/popover-polyfill@latest/dist/popover.min.js");
  }
</script>

Ограничение, связанное со стилями. Полифил не может определить настоящий псевдокласс :popover-open, поэтому вместо него применяется класс .\:popover-open. ОБЯЗАТЕЛЬНО. Объедините эти два правила с помощью :is() или :where(), иначе браузеры, не поддерживающие :popover-open, будут игнорировать правило целиком.

[popover]:is(:popover-open, .\:popover-open) {
  display: block;
}

В качестве альтернативы для устаревшего резервного варианта без полифила используйте position: fixed и вручную рассчитайте координаты с помощью getBoundingClientRect() или полагайтесь на позиционирование по умолчанию с помощью inset: auto, если это приемлемо для вашего случая.

Браузеры, не поддерживающие Popover API, также не поддерживают позиционирование привязки, поэтому подсказка будет появляться в центре экрана.