悬停时显示提示

用户希望在不完全改变上下文的情况下看到其他相关信息。当用户想要了解更多信息时,显示提示会很有用,例如提供术语定义、阐明仅图标按钮将执行的操作,或提供额外的表单字段指导。

创建提示

您可以通过向 <div> 或其他语义上合适的元素添加 popover="hint" 属性,创建具有所需行为的弹出式窗口。当用户打开提示时,系统会隐藏其他 popover="hint" 提示,但不会隐藏 auto 或 manual 提示。它还会处理嵌套提示的关闭。

它还提供轻关闭行为,因此当用户点击弹出式窗口外部或以其他方式将焦点移到弹出式窗口外部时,弹出式窗口就会关闭。

提示元素必须具有一个值唯一的 id 属性:

<!-- MANDATORY: The tooltip container `<div>` must have a `popover` attribute.
     the value of `"hint"` ensures it can be "light dismissed". -->
<div popover="hint" id="tooltip">Tooltip content</div>

用户通过将鼠标悬停在 <a> 或 <button> 元素上或将焦点移到这些元素上,来表达对其他信息的兴趣。该元素必须具有与提示的 id 属性匹配的 interestfor 属性。

<!-- The `interestfor` attribute can be applied to a `<button>` element: -->
<button interestfor="tooltip">Tooltip trigger</button>

<!-- The `interestfor` attribute can also be applied to an `<a>` element: -->
<a interestfor="tooltip" href="">Tooltip trigger</a>

触发器必须具有视觉指示器,以表明通过与触发器互动可以获取更多信息。

interestfor 内置无障碍功能

当您使用 interestfor 时,浏览器会为您处理辅助技术连接:

  • 隐式语义:具有 popover="hint" 的目标会获得 tooltip 的隐式最低角色。请勿自行设置 role="tooltip"。
  • 隐式关联:当目标为纯文本时,浏览器会通过 aria-describedby 将源元素与目标隐式关联;当目标包含互动内容时,浏览器会通过 aria-details 将源元素与目标隐式关联。请勿向触发器添加 aria-describedby 或 aria-details。
  • 互动内容:由于关联会在需要时切换到 aria-details,因此目标允许包含互动内容(例如“兴趣卡片”内的链接)。

无障碍限制 (WCAG 1.4.13)

即使 interestfor 处理了上述语义,您的实现也必须满足 WCAG 1.4.13(悬停或聚焦时显示的内容)的要求:

  • 可关闭:用户必须能够关闭提示,而无需移动指针悬停或键盘焦点(例如,通过按 Escape 键)。原生 popover 属性会自动管理此绑定。
  • 可悬停:指针必须能够移到提示内容本身上方,而不会导致提示消失。这样一来,使用放大工具的用户就可以安全地阅读提示文本。
  • 持久性:提示必须保持可见,直到悬停或焦点触发器被移除、用户明确将其关闭或其内容不再有效。

定位提示

提示使用 CSS 锚点定位进行定位。使用 interestfor 打开提示时,触发器元素会自动成为弹出式窗口的隐式锚点。

在支持原生锚点定位的现代浏览器中,您无需在触发器上定义 anchor-name,也无需在提示上定义 position-anchor。您可以使用 position-area 将提示直接相对于其隐式锚点进行定位,并使用 position-try-fallbacks 处理碰撞翻转:

[popover] {
  /* Implicit anchor from interestfor: no anchor-name or position-anchor needed */
  position-area: top;
  position-try-fallbacks: flip-block;
  margin: unset;
}

后备策略

兴趣调用器的回退和浏览器支持

必须使用 NPM 中的 interestfor polyfill 软件包有条件地对兴趣调用器进行 polyfill。建议捆绑 polyfill,而不是使用 CDN。

强制要求:通过检查 HTMLButtonElement.prototype 上的 interestForElement 属性来检测功能,并且仅在缺少原生支持时加载 polyfill。

<script type="module">
  if (!HTMLButtonElement.prototype.hasOwnProperty("interestForElement")) {
    // CDN link only used for example, prefer bundling.
    await import("https://unpkg.com/interestfor@latest");
  }
</script>

CSS Polyfill 注意事项

使用 polyfill 时,CSS API 会略有变化,以弥补原生伪类和属性支持的不足:

  • 伪类:polyfill 无法定义真正的 :interest-source 和 :interest-target 伪类,因此它会改为应用 .interest-source 和 .interest-target 类。强制性:将它们与 :is() 结合使用,以同时支持原生状态和 polyfill 状态。
/* Styles to apply when the effect is being previewed */
:is(:interest-source, .interest-source) { /* ... */ }
:is(:interest-target, .interest-target) { /* ... */ }
  • CSS 属性:对于 interest-delay、interest-delay-start 和 interest-delay-end,polyfill 需要使用 CSS 变量,因为它无法在所有浏览器中直接读取自定义属性。
/* Adjust the start and end delay for interest invokers */
[interestfor] {
  --interest-delay-start: 0.2s;
  interest-delay-start: var(--interest-delay-start);
  --interest-delay-end: 0.1s;
  interest-delay-end: var(--interest-delay-end);
}

Popover 的回退和浏览器支持

Popover API 主要是一种渐进增强技术,但其核心行为(顶层提升、轻触关闭和 popovertarget 调用)没有仅限 CSS 的等效项。旧版浏览器需要 polyfill,如果您不想提供 polyfill,则需要手动后备方案。

Polyfill:如需在旧版浏览器中支持 popover 属性,请有条件地加载 @oddbird/popover-polyfill。强制性要求:通过检查 HTMLElement.prototype 上的 popover 属性来检测功能,并且仅在缺少原生支持时加载 polyfill,请勿无条件加载。

使用打包工具或导入映射:

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

如果没有打包器,请从 <script type="module"> 内的 CDN 导入:

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

样式注意事项:polyfill 无法定义真正的 :popover-open 伪类,因此它会改为应用 .\:popover-open 类。强制性:使用 :is() 或 :where() 将两者结合,否则缺少 :popover-open 的浏览器会舍弃整个规则:

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

或者,对于没有 polyfill 的旧版后备方案,请使用 position: fixed 并通过 getBoundingClientRect() 手动计算坐标,或依赖 inset: auto 的默认定位(如果该用例可以接受)。

在支持 popover 但不支持 popover="hint" 的浏览器中,popover-polyfill 不会 polyfill 提示行为。对于这些浏览器,通过悬停打开第二个提示时,通过聚焦打开的提示可能会保持打开状态。

为了支持不支持锚点定位的浏览器,您可以使用 @oddbird/css-anchor-positioning polyfill。或者,您也可以使用 @supports 指定后备位置。

使用 @oddbird/css-anchor-positioning polyfill 支持旧版浏览器时,请注意,该 polyfill 不支持隐式锚点或弹出式窗口上的 position-area。在触发器上明确设置 anchor-name,在弹出式窗口上设置 position-anchor,并使用 anchor() 边衬区函数(而非 position-area)进行定位:

<!-- MANDATORY: Conditionally install the anchor positioning polyfill -->
<script type="module">
  if (!("anchorName" in document.documentElement.style)) {
    await import("https://unpkg.com/@oddbird/css-anchor-positioning");
  }
</script>
button[interestfor="tooltip-attrs"] {
  /* MANDATORY: Each trigger and popover pair must have a unique anchor name, referenced by `anchor-name` on the trigger and `position-anchor` on the popover. */
  anchor-name: --tooltip-attrs;
}
#tooltip-attrs {
  position-anchor: --tooltip-attrs;
  /* If using the anchor positioning polyfill with a popover, DO use `anchor()` functions, and not `position-area. */
  top: anchor(bottom);
  left: anchor(right);
  margin: unset;
}