aria-modal="true"必须与role="dialog"配对使用,需手动处理焦点围栏、背景aria-hidden和标题关联,否则屏幕阅读器无法正确识别模态框。

只加 aria-modal="true" 不够,必须和 role="dialog" 配对使用,且要手动处理焦点、遮罩层语义和背景隔离,否则屏幕阅读器用户根本感知不到模态框已打开。
aria-modal="true" 必须和 role="dialog" 同时出现
单独写 aria-modal="true" 在任意元素上(比如 div 或遮罩层)是无效的。WAI-ARIA 规范明确要求:该属性仅对设置了 role="dialog" 或 role="alertdialog" 的元素起作用。
常见错误现象:
- 写了
aria-modal="true"但没写role="dialog",NVDA/VoiceOver 仍朗读背景内容 - 把
aria-modal="true"加在遮罩层(overlay)上,对话框容器反而没设,辅助技术完全忽略模态上下文 - 用了
role="modal"这类非法 role 值,导致整个 ARIA 声明被浏览器静默丢弃
实操建议:
-
aria-modal="true"必须写在模态框根容器上,例如:<div id="my-dialog" role="dialog" aria-modal="true"> <li>值只能是 <code>"true";aria-modal="false"等价于不写,且部分读屏会直接忽略 - 如果模态框支持“非模态”切换(如用户勾选“后台运行”),需用 JS 动态设置/移除该属性,并同步更新焦点和
aria-hidden - 只设了
aria-modal="true",但没给<main id="main-content"></main>加aria-hidden="true",屏幕阅读器仍能 Tab 到页脚链接 - 把
aria-hidden="true"加在模态框自身或遮罩层上,导致整个对话框对辅助技术不可见 - 关闭模态框后忘记移除
aria-hidden,后续页面操作全部失焦 - 打开时执行:
document.getElementById('main-content').setAttribute('aria-hidden', 'true') - 若页面有多个语义区块(如
<aside></aside>、自定义侧边栏),都要一并设aria-hidden="true" - 关闭时必须遍历还原:
el.removeAttribute('aria-hidden')或设为'false'(推荐前者) - 不要给
加aria-hidden——这会让读屏彻底丢失上下文 - 打开后立刻获取首个可聚焦元素:
const focusables = Array.from(modal.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')) - 调用
focusables[0]?.focus();若为空,给 modal 根元素设tabindex="-1"后再.focus() - 监听
modal上的keydown,当e.key === 'Tab'时:- 计算当前
document.activeElement在focusables中的索引 - 按 Shift 是否按下决定跳向前/后一个;越界时
e.preventDefault()并.focus()到首/尾
- 计算当前
- 关闭前,必须把焦点切回触发按钮(不是
document.body),推荐用queueMicrotask(() => triggerBtn.focus()) -
aria-labelledby必须指向一个**可见、非aria-hidden、非display: none的标题元素**,例如:<h2 id="dlg-title">删除文件?</h2> - ID 必须严格匹配:
aria-labelledby="dlg-title",不能拼错或漏引号 - 若对话框含说明性副文本(非标题),用
aria-describedby指向对应<p id="dlg-desc"></p>;没有就别写,别指向空 ID - 避免用
aria-label替代aria-labelledby——前者是静态字符串,无法响应 DOM 更新(比如多语言切换)
背景内容必须动态加 aria-hidden="true"
aria-modal="true" 不会自动隐藏背景,它只声明“这是模态区域”,真正屏蔽背景可访问性的,是给主内容区(如 <main></main>、<header></header>)加 aria-hidden="true"。
常见错误现象:
实操建议:
焦点陷阱不能靠 aria-modal 自动实现
aria-modal="true" 不拦截 Tab 键,也不阻止 Shift+Tab 跳出。所有主流浏览器(包括 Safari 17.4+)都不保证原生焦点围栏,必须手写逻辑。
实操建议:
标题和描述必须显式关联
只写 role="dialog" + aria-modal="true",屏幕阅读器只会说“对话框”,不会读标题。用户不知道这是“登录框”还是“删除确认”。
实操建议:
最易被忽略的一点:遮罩层(overlay)本身不该有语义。别给它加 role="dialog" 或 aria-modal,更别设 aria-hidden="true" ——它纯属视觉装饰,用 role="presentation" 或干脆不加任何 ARIA 最安全。











