aria-modal="true" 单独无效,仅是语义提示;必须与 role="dialog" 同时使用,并配合动态添加 aria-hidden="true" 到非模态区域及 JS 焦点陷阱才能实现真正无障碍隔离。

aria-modal="true" 本身不能防止读屏软件读取背景层——它只是一种语义声明,不是屏蔽指令。真正起作用的是 aria-hidden="true" 动态加在背景容器上,配合 role="dialog" 才能形成有效隔离。
为什么aria-modal="true单独用没用
这个属性只是告诉 NVDA、VoiceOver 这是个模态上下文,但 Safari 全版本、旧版 Firefox 和部分 NVDA 配置下会直接忽略它。实测中,用户打开弹窗后,VoiceOver 仍会朗读页脚的链接、导航栏的菜单项,甚至跳到 document.body 上开始从头读起。
-
aria-modal="true"必须和role="dialog"同时出现在模态框根元素上,缺一不可 - 它不改变 DOM 可访问性树,也不影响焦点行为,纯属“提示型”属性
- 如果模态框没设
role="dialog",aria-modal="true"会被辅助技术当成无效值处理
真正屏蔽背景的唯一可靠方式是aria-hidden="true"
必须手动把 aria-hidden="true" 加到所有非模态区域(比如 <main>、<header>、<aside>),而不是粗暴地加在 <body> 上——某些屏幕阅读器遇到 <body aria-hidden="true"> 会直接跳过整页,连模态框都读不到。
- 推荐结构:用一个包裹主内容的
<div id="app">,打开模态框时给它加aria-hidden="true",关闭时移除 - 不要给模态框自身加
aria-hidden="true",否则整个对话框对读屏软件“消失” - 遮罩层(overlay)一般不需要
aria-hidden,用role="presentation"或干脆不加更稳妥
Tab 键跑出模态框?那是因为没做焦点陷阱
即使 aria-modal="true" 和 aria-hidden="true" 都设对了,键盘用户按 Tab 仍可能跳到背景里——因为浏览器原生 Tab 行为完全不受这两个属性控制。必须靠 JS 拦截 keydown,手动管理焦点循环。
立即学习“前端免费学习笔记(深入)”;
- 缓存模态框内所有可聚焦元素:
Array.from(modal.querySelectorAll('button, a[href], input, select, textarea, [tabindex]:not([tabindex="-1"])')) - 监听
Tab和Shift+Tab,当焦点在最后一个元素再按 Tab 时,event.preventDefault()并 focus 第一个;反之亦然 - 打开模态框后立刻
firstFocusableElement.focus(),别等 CSS 动画结束 - 关闭时必须将焦点恢复到触发按钮(比如
document.getElementById("open-modal-btn")),不能document.body.focus()
最易被忽略的一点:所有这些操作必须严格同步——aria-hidden="true" 要在焦点 trap 启动前就加好,否则读屏软件可能在焦点还没锁住时就开始朗读背景内容。顺序错了,整个无障碍逻辑就崩了。



















