dialog 标签本身不能直接提升无障碍水平,它只是起点;真正起作用的是正确调用 showModal()、手动管理焦点、弥补 Safari 焦点围栏缺失、配合 aria-hidden 控制背景。

dialog 标签本身不能直接提升无障碍水平,它只是个起点。真正起作用的是你如何调用 showModal()、怎么管理焦点、是否补足 Safari 的缺口,以及是否配合 aria-hidden 控制背景。
为什么 dialog.showModal() 在 Safari 中会失效
Safari(所有已知版本)不实现焦点围栏,Tab 键仍能跳到页脚、导航栏甚至 body。这不是“偶尔失灵”,是原生行为缺失——浏览器没把焦点流真正锁进 dialog 内部。
- 必须在
showModal()后立刻用requestAnimationFrame(() => firstFocusable.focus())聚焦首个可聚焦元素(button、input、[tabindex="0"]),不能依赖autofocus - 必须手动监听
keydown,捕获Tab和Shift+Tab,在可聚焦元素列表内循环,不能靠浏览器自动 - 关闭前要缓存触发按钮(如
openBtn),关闭后检查它是否还存在且可聚焦,再调.focus(),不是document.body.focus()
点击 backdrop 关闭对话框的兼容写法
dialog 的 ::backdrop 默认不响应 click 事件,这是规范行为,不是 bug。很多开发者漏掉这步,用户就卡死在模态框里。
- 基础监听:
dialog.addEventListener('click', e => { if (e.target === dialog) dialog.close(); }) - Safari 15.4–16.3 存在
e.target总是body的问题,需 fallback 到坐标判断:检查e.clientX/e.clientY是否落在dialog.getBoundingClientRect()区域外 - 切勿给
dialog设pointer-events: none,这会破坏原生焦点锁定机制
背景内容禁用必须双保险
aria-modal="true" 在旧版 Firefox 和部分 Safari 中会被忽略,屏幕阅读器照读背景内容。单靠这个属性,等于没锁住信息流。
立即学习“前端免费学习笔记(深入)”;
- 模态框根节点设
role="dialog"+aria-modal="true" - 同时给包裹主内容的容器(如
<div id="app">)动态加aria-hidden="true",而不是直接操作<body> - 关闭时务必移除
aria-hidden="true",否则后续内容无法被读取
最易被忽略的一点:所有可聚焦元素必须是真正可交互的。一个 <h3 tabindex="0"> 或 <div tabindex="0"> 不会被 .focus() 激活,浏览器只聚焦原生可聚焦标签或显式声明 tabindex="0" 且内容可操作的元素。别让第一个 focus() 调用静默失败。



















