role="dialog"是屏幕阅读器识别弹窗语义的必要声明,必须搭配aria-labelledby和aria-modal="true",并配合焦点管理,否则视障用户无法感知弹窗存在或正确操作。

role="dialog" 不是可选装饰,而是屏幕阅读器识别弹窗语义的必要声明;漏掉它,视障用户根本不知道当前弹出了模态框。
为什么必须显式写 role="dialog"
浏览器和辅助技术(如 NVDA、VoiceOver)不会自动推断一个 div 是对话框——哪怕它有遮罩层、居中样式、Esc 关闭逻辑。只有明确写了 role="dialog",屏幕阅读器才会:
- 朗读“对话框”角色名 +
aria-label或aria-labelledby指向的标题 - 自动将焦点限制在弹窗内(需配合 JS 实现)
- 在关闭后将焦点返回触发按钮(需手动管理)
没这个 role,它就只是个普通 div,用户听到的可能是“空白区域”或直接跳过。
role="dialog" 必须搭配的两个属性
光写 role="dialog" 不够,缺一不可:
-
aria-labelledby:指向弹窗内唯一可见标题的id(推荐),例如→确认删除?
aria-labelledby="modal-title" -
aria-modal="true":告诉辅助技术“此元素外的内容应被忽略”,避免背景内容被误读(注意:IE 不支持,但现代浏览器都 OK)
别用 aria-label 替代 aria-labelledby——除非标题纯图标无文字,否则会绕过视觉标题,造成信息不一致。
常见错误:用 role="dialog" 却没管焦点
声明了角色,但没做焦点控制,等于只装了门牌没装门锁。用户 Tab 键仍能跳出弹窗,或首次打开时焦点停在页面顶部:
- 弹窗显示后,立即
focus()到第一个可交互元素(如确认按钮或输入框) - 实现焦点环捕获:监听
keydown,拦截 Tab 键,循环聚焦弹窗内可 tab 元素 - 关闭弹窗时,
focus()回触发它的按钮(存好引用,别依赖document.activeElement)
没这些,role="dialog" 就是半残废——语义有了,行为没跟上。
别把非模态浮层也标成 dialog
下拉菜单、Tooltip、Toast 提示、侧边栏抽屉……这些都不是 role="dialog" 的使用场景:
- 下拉菜单 →
role="menu"+role="menuitem" - Tooltip →
role="tooltip"+aria-describedby - Toast →
role="status"或role="alert"(后者会中断当前朗读)
滥用 role="dialog" 会让屏幕阅读器反复报“对话框”,引发困惑。角色必须严格匹配真实交互意图。
最常被忽略的是 aria-modal="true" 和焦点重定向——很多人写了 role 就以为完事了,结果测试时发现 NVDA 完全不朗读标题,或者关闭后焦点丢了。这两个点不补上,无障碍就卡在门口。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











