原生 dialog 元素必须调用 showModal() 才能触发模态行为,包括遮罩、背景禁用、自动聚焦和 Escape 响应;自定义标签如 <_DIALOG> 无效且无法注册,封装需基于原生 <dialog> 或降级为 ARIA dialog。

dialog 元素不能直接当组件模板用
写 <_DIALOG> 或 <my-dialog> 不会触发原生模态行为,浏览器把它当未知标签处理,生成 HTMLUnknownElement 实例——它没有 showModal()、不响应 Escape、document.querySelector('dialog') 也匹配不到(除非你查 '_DIALOG')。W3C 要求自定义元素名必须含短横线,_DIALOG 连注册都失败,报 DOMException: The name "_DIALOG" is not a valid custom element name。真要封装,只能基于原生 <dialog> 做轻量包装,或退回到 <div role="dialog"> + ARIA 手动控制。
原生 dialog 必须调用 showModal() 才生效
<dialog open> 或只调 show() 都没用:前者只是让元素可见,不加遮罩、不锁背景、::backdrop 不渲染;后者连遮罩都不加,用户照常 tab 切换、点按钮、输文字。真正起作用的只有 showModal(),它会做四件事:插入灰色遮罩、禁用背景交互、把焦点移到第一个可聚焦子元素、响应 Escape 和点击遮罩自动触发 close 事件。实操中必须确保 dialog 是 <body> 的直接子元素,否则 Safari 和部分 Chrome 下 ::backdrop 渲染失败。
封装时 focus 和 close 事件最容易漏掉
原生 dialog 关闭后 open 属性变 false,但 DOM 还在,焦点可能丢失——尤其 Safari 首次打开常不自动聚焦,需 setTimeout(() => dialog.focus(), 0) 强制补焦;关闭后得手动 document.body.focus(),否则键盘操作卡住。别监听 click 并 stopPropagation() 来阻止 backdrop 关闭,这破坏屏幕阅读器和 Escape 行为。正确做法是保留 backdrop 点击触发 close,然后在 dialog.addEventListener('close', handler) 里处理业务逻辑(比如清空表单、恢复按钮状态)。
兼容性兜底必须自己写,polyfill 不可靠
dialog-polyfill 在 Safari 15.4 之前能补基本显隐,但无法还原 showModal() 的焦点锁定、backdrop 拦截和 Esc 响应——这些是渲染层和事件机制决定的,polyfill 模拟不了。实操建议:用 if ('showModal' in HTMLDialogElement.prototype) 检测,不支持时降级为 <div role="dialog" aria-modal="true"> + 手动管理 tabindex、aria-hidden 和遮罩层显隐。别指望一个 import 就搞定所有浏览器,老 Edge、iOS 15.3 以下、微信内置浏览器都得单独测试。
立即学习“前端免费学习笔记(深入)”;



















