aria-modal="true" 必须与 role="dialog" 或 role="alertdialog" 配合使用才生效,单独设置无效;它仅影响屏幕阅读器上下文隔离,不自动限制键盘焦点,需手动实现焦点陷阱。

aria-modal="true" 必须配合 role="dialog" 或类似语义角色
单独写 aria-modal="true" 没有效果,屏幕阅读器会直接忽略。它只在有明确模态上下文(如 role="dialog"、role="alertdialog")时才被识别为“阻断背景焦点”。
常见错误是给一个 <div> 直接加 <code>aria-modal="true" 却不设 role,结果键盘仍能 tab 到背景元素,NVDA/JAWS 也不宣布“对话框已打开”。
- ✅ 正确用法:
<div role="dialog" aria-modal="true" aria-labelledby="title"> <li>❌ 无效写法:<code><div aria-modal="true">(无 role,等同于没写) <li>⚠️ 注意:不要用 <code>role="modal"—— 这不是合法 ARIA role,会导致验证失败和读屏异常 - 用
element.focus()主动聚焦,别依赖 autofocus 属性(兼容性差,且 Safari 对 dialog 内 autofocus 支持不稳定) - 监听
keydown时检查e.key === 'Tab',配合document.activeElement判断当前焦点位置 - 避免用
inert属性替代焦点管理 —— 目前 Chrome 仅部分支持,Firefox/Safari 尚未实现 - ✅ 推荐:
<dialog id="myDialog"></dialog>+myDialog.showModal() - ⚠️ 混用风险:若手动给
<dialog></dialog>加aria-modal="false",可能覆盖原生行为,导致焦点管理失效 - ⛔ 不要为了“兼容旧浏览器”而在
<dialog></dialog>上强行降级为 div + aria-modal —— 这反而破坏了原生语义和可访问性修复机制 - 用键盘连续按 Tab,确认焦点无法到达弹窗外的
<button></button>、<a></a>或input(尤其注意 footer 中的链接) - 用 VoiceOver(macOS)或 NVDA(Windows)打开弹窗后,确认读屏器播报类似 “对话框,标题内容,模态” 的提示,而不是静默进入或误读为普通区域
- 移动端 Safari 测试:双指滑动有时会意外滚动背景,需额外加
body { overflow: hidden }并在关闭时恢复
焦点管理比 aria-modal 更关键
aria-modal="true" 只影响屏幕阅读器的“上下文隔离”,**不自动限制键盘焦点**。浏览器原生仍允许 Tab 焦点逃逸到背景。必须手动实现焦点陷阱(focus trap)。
典型做法是在弹窗打开后:把焦点移到第一个可聚焦子元素(如按钮或输入框),并监听 keydown 事件拦截 Shift+Tab / Tab,强制循环在弹窗内。
与
如果用的是原生 <dialog></dialog> 元素,aria-modal 是冗余的。现代浏览器(Chrome 97+、Firefox 99+)调用 dialog.showModal() 后,会自动添加 aria-modal="true" 并启用焦点锁定。
测试时容易漏掉的两个真实场景
即使代码写对了,实际用户仍可能绕过模态限制。重点验证以下两点:
真正卡住人的往往不是 aria-modal 设没设,而是焦点没锁住、语义角色缺失、或者测试只在桌面 Chrome 做了就上线。











