Popover API:焦点、轻量关闭与无障碍回退
围绕 popover=auto 或 manual 定义 top layer、Escape、外部点击、焦点、事件和可用回退的无障碍契约。
BotBrowser Team
BotBrowser 支持重放获授权的 fixture,记录打开、关闭和焦点的可见结果;但不能保证辅助技术行为,也不会把回退实现变成浏览器的已发布支持。涉及指针与键盘的流程,还应结合指针事件无障碍检查和浏览器自动化无障碍测试。
TL;DR
popover="auto"适合可轻量关闭的非模态表面;manual适合由应用拥有每个关闭决定的表面。- top layer 不会自动创建焦点陷阱、无障碍名称或对话框语义。
- 把
Escape、外部点击、焦点返回和背景inert作为独立结果测试。 beforetoggle可以取消转换;toggle报告已提交的状态,回退也要保留同一结果。
内容导航
选择状态模型
WHATWG HTML 规范定义 popover 属性和显示、隐藏算法。popover="auto" 适合菜单、提示和过滤器等非模态表面;浏览器会管理自动 popover 栈。popover="manual" 把关闭决定留给应用,适合多个相关表面需要同时存在或由“完成”按钮结束的流程。
不要把该属性当作 <dialog> 的同义词。Popover 不会自动成为模态,不会自动捕获焦点,也不会自动获得无障碍名称。使用真正的 button、popovertarget 和描述内容的名称。只有在确实实现菜单键盘行为时才添加菜单角色。
MDN Popover API 指南介绍 showPopover()、hidePopover() 和 togglePopover()。选择命令式路径前先做特性检测,并让界面说明内容、关闭方式以及背后仍可用的任务。
Top layer 与轻量关闭
显示后,popover 会进入 top layer。auto 提供浏览器管理的轻量关闭,但不保证在缩小视觉 viewport、缩放、虚拟键盘或 iframe 中仍有合适位置。分别测试 Escape 和外部点击。manual 必须有关闭按钮和明确的事件路径;不要使用透明覆盖层吞掉点击却让页面在语义上保持可用。
Chrome 的 Popover API 介绍说明这些目标,但不是所有浏览器、嵌入式 WebView 或辅助技术行为完全相同的证明。记录版本和 viewport,断言可见结果,不要断言具体绘制顺序。
焦点和 inert 是不同契约
显示 popover 不会决定焦点去哪里。根据它是建议、菜单还是编辑器,可以保留触发器焦点,或移动到第一个有意义的控件。保存打开者,把焦点返回仍连接且可用的元素;若打开者消失,使用约定的标题或下一步控件。不要对已脱离 DOM 的节点调用 focus()。
检查键盘、指针和辅助技术是否都能到达预期控件。manual 可以有意让背景保持可用;真正的模态任务更适合 <dialog> 与 showModal()。WCAG 键盘指南和focus-visible 指南要求检查名称、角色、状态、焦点指示器和下一步操作。
观察 beforetoggle 与 toggle
beforetoggle 在转换前触发,可以拒绝转换或准备焦点决定。toggle 在状态提交后触发。快速转换可能合并;记录 opened 或 closed-by-escape 这样的有界状态,不要保存页面文本或每次点击的审计。
const trigger = document.querySelector('#filters-button');
const panel = document.querySelector('#filters-panel');
let opener = null;
trigger.addEventListener('click', () => {
opener = document.activeElement;
panel.togglePopover();
});
panel.addEventListener('beforetoggle', event => {
if (event.newState === 'open' && !trigger.isConnected) event.preventDefault();
});
panel.addEventListener('toggle', event => {
if (event.newState === 'closed' && opener?.isConnected) opener.focus();
});
这是协调模式,不是每个浏览器事件时序相同的承诺。取消后表面和焦点仍要可用;没有事件 API 时,回退也应产生等价的应用状态。
构建可用回退
先检测 API,再使用普通按钮、初始隐藏区域和带 aria-controls 的关闭按钮。保留相同内容与焦点顺序。若任务本来是模态,应采用受支持的对话框模式,不要把 manual 假装成模态。处理方法缺失、焦点操作被拒绝、打开者被删除和 viewport 改变。BotBrowser 可以在获授权的上下文比较声明的可见状态,但不能认证读屏器播报或每个 build 的原生 top layer。
运行有界交互矩阵
| 路径 | 预期结果 | 需要调查的失败 |
|---|---|---|
键盘调用,auto | 打开,焦点可见,Escape 关闭 | 焦点消失 |
外部点击,auto | 只关闭一次并返回焦点 | 重复清理 |
关闭按钮,manual | 应用隐藏表面并恢复打开者 | 没有关闭所有者 |
| API 不可用 | 内容和键盘路径等价 | 回退不可达 |
| viewport 变窄 | 关闭出口可见且文字可读 | 唯一出口被裁切 |
在缩放、减少动效、触控和窄 viewport 下重复这些行。检查 document.activeElement、无障碍名称、焦点指示器和每次关闭后的下一步。fixture 保持合成,只记录解释失败所需的状态标签。
如果 popover 还需要跟随移动的 trigger,请阅读 CSS anchor positioning 边界指南,把 anchor-name、fallback 候选和 clipping 与焦点责任分开测试。
实践结论
根据关闭所有者选择 auto 或 manual,再分别测试 top layer、轻量关闭、Escape、焦点、inert 和回退。WHATWG、MDN 与 Chrome 说明平台;BotBrowser 可重复获授权的可见 fixture,语义和无障碍恢复仍由应用负责。
来源
FAQ
popover="auto" 是模态对话框吗?
不是。需要模态语义时使用 <dialog> 和 showModal()。
top layer 会自动移动焦点吗?
不会。明确焦点目的地、返回位置以及打开者消失时的回退。
每个 popover 都应该外部点击关闭吗?
不应该。auto 提供轻量关闭;manual 把决定交给代码。关闭操作必须可发现。
本文属于指南: Web 平台能力面:一致性复核地图