dialog.showmodal() 是唯一能触发原生焦点捕获的调用,它自动聚焦首个可聚焦子元素、限制tab键循环、支持esc和遮罩关闭;而show()无此能力,且无障碍无效。

dialog.showModal() 是唯一能触发原生焦点捕获的调用
调用 dialog.show() 不会限制 Tab 键、不阻止背景交互、不自动聚焦,它只是让 <dialog></dialog> 可见——对无障碍完全无效。只有 dialog.showModal() 才会激活浏览器内置的模态流:焦点自动落到第一个可聚焦子元素,Tab 键天然在内部循环,Esc 和 backdrop 点击自动关闭并恢复焦点。
- 必须确保模态框内至少有一个可聚焦元素(
<button></button>、<input>、或显式tabindex="0"的元素),否则showModal()聚焦失败,焦点可能停在页面顶部 - 不要在
showModal()后立刻调用.focus()—— 浏览器已处理,强行干预反而导致 Safari 中焦点跳动或 NVDA 重复朗读 - 若需自定义初始焦点(比如跳过标题直接聚焦输入框),应在
showModal()后用requestAnimationFrame延迟一次再调用focus(),避免与 UA 默认行为冲突
aria-modal="true" 在 dialog 元素上是冗余但必要的兼容补丁
<dialog></dialog> 原生已隐式具备模态语义,现代 Chrome/Firefox/Edge 会自动添加 aria-modal="true" 并管理 aria-hidden。但 Safari 完全不支持 <dialog></dialog>,旧版 Firefox(aria-modal="true"。
- 不能省略:漏掉它,NVDA 在 Firefox 中可能读作“普通区域”,而非“对话框”
- 不能只靠它:即使写了
aria-modal="true",若没调用showModal(),焦点仍会逃逸;该属性只是声明意图,不替代行为实现 - 搭配
role="dialog"无害但非必需;<dialog></dialog>已有内置 role,重复声明可能在极少数 AT 中引发冗余播报
dialog 元素的 aria-labelledby 必须指向真实可见标题
<dialog></dialog> 不会自动关联标题;若没设 aria-labelledby,VoiceOver 和 NVDA 大概率只报“对话框”,不读标题文字,用户无法判断当前弹窗用途。
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
- 标题必须是页面中真实存在的、非
display: none或visibility: hidden的元素(推荐用<h2></h2>或<h3></h3>) - ID 必须唯一且拼写准确;写成
aria-labelledby="modal-title-1"但实际是id="modal-title",会导致朗读静默 - 禁用
aria-label替代:它绕过视觉标题,造成视障用户看到的和听到的不一致,WCAG 明确不推荐
Safari 和部分旧浏览器下 dialog 几乎不可用,polyfill 也不完美
截至 2026 年 9 月,Safari 仍未实现 <dialog></dialog>,官方 roadmap 无明确时间表;Firefox 需开启 dom.dialog_element.enabled,且 backdrop 样式渲染异常;Google 的 dialog-polyfill 能补基本显示,但焦点环、Esc 行为、屏幕阅读器语义同步均存在偏差。
- polyfill 无法模拟原生
showModal()的焦点捕获机制,仍需手动监听keydown实现 Tab 循环 - 它不会自动设置
aria-hidden到背景容器,必须自己用 JS 控制document.body或 wrapperdiv的aria-hidden - 测试发现:JAWS + IE11 组合下,polyfill 版本常被识别为
role="application"而非dialog,导致朗读逻辑错乱
真正落地时,别把 <dialog></dialog> 当成“开箱即用”的无障碍解法——它的原生能力只在 Chrome/Edge 最新版里稳定,在其他环境里,你得重写一套焦点管理逻辑,和不用它没本质区别。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










