aria-modal="true"本身不屏蔽背景,必须与aria-hidden="true"配合使用,且仅对role="dialog"或role="alertdialog"元素生效;二者缺一不可,还需手动焦点管理才能实现完整无障碍模态。

aria-modal="true" 本身不直接“限制读屏工具读取下层”,它只是向辅助技术声明:“这个区域是模态的,背景内容应被逻辑隔离”。真正起屏蔽作用的是 aria-hidden="true" 动态加在背景容器上,aria-modal="true" 和 aria-hidden="true" 必须配合使用,缺一不可。
role="dialog" + aria-modal="true" 是语义前提
只写 aria-modal="true" 没用——它必须挂在一个明确声明模态角色的元素上,通常是 role="dialog" 或 role="alertdialog"。其他 role(比如 role="modal")不合法,屏幕阅读器会直接忽略该属性。
-
aria-modal="true"只对设置了合法 role 的元素生效;设在div或section上但没加role,等于没写 - 值必须是字符串
"true";写成false、1或省略,都等同于未声明 - 它不控制视觉遮罩、不锁焦点、不阻止 Tab 键跳出——这些全得靠 JS 补足
aria-hidden="true" 才真正屏蔽背景内容
模态框打开时,必须动态给所有非模态区域(如 <main>、<header>、侧边栏等)加上 aria-hidden="true";关闭时立刻移除或设为 "false"。不能加在模态框自身上,否则整个对话框会被读屏器跳过。
- 常见误操作:给
<body>加aria-hidden="true"—— 这会让整页从可访问树中消失,用户“看不见也听不到”任何东西 - 遮罩层(overlay)一般不需要
aria-hidden="true",用role="presentation"更合适,或干脆不加 ARIA(纯装饰) - 背景容器要选得准:不是整个
<body>,而是模态框之外的、有语义的主内容区块
为什么不能只靠 aria-modal="true"
实测中,仅设 aria-modal="true" 后,Safari(包括 iOS/macOS 全版本)、旧版 Edge 和部分 NVDA 配置下,屏幕阅读器仍可能朗读背景页脚或导航栏——因为 aria-modal 是个“建议性语义”,不是强制围栏。
立即学习“前端免费学习笔记(深入)”;
- 现代读屏器(NVDA/JAWS/VoiceOver)会据此自动隐藏父级中
aria-hidden="false"以外的内容,但兼容性不统一 - 手动加
aria-hidden="true"是兜底手段,尤其针对旧环境和特殊配置 - 二者组合才是 WCAG 2.1 要求的“完全模态”实现基础
焦点管理不到位,读屏照样乱
即使 aria-modal="true" 和 aria-hidden="true" 都到位,如果没做焦点控制,读屏用户仍可能卡在背景、Tab 出框、或首次打开不聚焦——这时“屏蔽”就形同虚设。
- 打开后必须立刻调用
firstFocusableElement.focus(),不能等 CSS 动画结束 - 要缓存模态框内所有可聚焦元素:
Array.from(modal.querySelectorAll('button, a[href], input, select, textarea, [tabindex]:not([tabindex="-1"])')) - 拦截
Tab和Shift+Tab,手动循环焦点;避免用不存在的伪类:focusable - 关闭后焦点必须回到触发源(如
document.getElementById('open-btn')),而不是document.body
真正让读屏工具“看不见遮罩下层”的,不是单个属性,而是一组协同动作:语义声明(role+aria-modal)、动态屏蔽(aria-hidden)、以及精确的焦点接管。漏掉任意一环,辅助用户就可能迷失在不可见也不可读的空白里。



















