dialog 模态框
<dialog> 是 HTML 原生模态框元素,调用一行 showModal() 就能获得焦点陷阱、Esc 关闭、顶层遮罩三件套,Chrome 37、Firefox 98、Safari 15.4 起全部支持,2022 年之后的新项目没有理由再引第三方模态框库。
dialog 模态框怎么写最小可用版本?
结论:一个 <dialog> 加一个 <form method="dialog">,十行以内就能跑通「打开—确认—取值」的完整链路。
<dialog id="confirm">
<form method="dialog">
<p>确认删除这条记录?</p>
<button value="cancel">取消</button>
<button value="ok">确定</button>
</form>
</dialog>
const d = document.getElementById('confirm');
d.showModal();
d.addEventListener('close', () => {
if (d.returnValue === 'ok') {/* 执行删除 */}
});
关键点在 method="dialog":表单提交不会刷新页面,而是直接关闭对话框,并把被点击按钮的 value 写进 d.returnValue。这是原生能力,不需要监听按钮 click 再手动 close()。
showModal() 和 show() 有什么区别?
结论:showModal() 打开的是真正的模态,show() 只是把元素显示出来,两者行为完全不同。
showModal() 会做四件事:把对话框放入 top layer(渲染层级高于所有 z-index)、画出 ::backdrop 遮罩、让页面其余内容变为 inert(不可点击不可聚焦)、把 Tab 焦点锁在框内并支持 Esc 关闭。show() 这四件一件都不做,对话框和普通 div 没有区别,用户仍能点到背景页面。
所以除非你在做非阻塞的提示条,一律用 showModal()。另外注意:属性写法 <dialog open> 等同于用 CSS 显示,属于非模态,别指望它挡住背景。
怎么自定义遮罩和打开关闭动画?
结论:遮罩用 ::backdrop 伪元素改,动画必须处理 display 的离散切换,否则过渡不会生效。
dialog::backdrop { background: rgba(0,0,0,.5); backdrop-filter: blur(4px); }
对话框关闭时 display 变为 none,而 display 是不可过渡的离散属性,直接写 transition: opacity .2s 只会看到瞬间消失。解法是 Chrome 117+ 支持的 allow-discrete 加 @starting-style:
dialog {
opacity: 0; transform: translateY(8px);
transition: opacity .2s, transform .2s,
display .2s allow-discrete, overlay .2s allow-discrete;
}
dialog[open] { opacity: 1; transform: none; }
@starting-style { dialog[open] { opacity: 0; transform: translateY(8px); } }
overlay 属性负责让对话框在关闭动画播完后才从 top layer 移除,漏掉它会出现动画被打断的闪跳。
dialog 有哪些常见坑?
结论:报错和「不好用」绝大多数集中在这四个点上。
第一,对同一个已打开的 dialog 再次调用 showModal() 会抛 InvalidStateError,必须先 close() 或改用 d.open 判断。第二,关闭后默认 display: none,如果你用 display: block 强行覆盖,模态框就会常驻显示。第三,Esc 键不会触发 close 之前的事件流是 cancel(此时可 event.preventDefault() 拦截),需要二次确认的场景要挂 cancel 而不是 close。第四,无障碍要显式标注,写法是 aria-labelledby="dialog-title" 指向框内标题元素,屏幕阅读器会把它读作对话框名称。
dialog 和 popover 该选哪个?
结论:需要用户必须响应、要填表或确认的用 dialog;轻量、可点外部关闭的浮层用 popover 属性。
popover 是 2023 年进入 Baseline 的另一套原生浮层机制,默认非模态、点击外部自动关闭、支持 popover="auto" 的自动互斥。它不锁焦点也不遮罩,适合下拉菜单、气泡提示。判断标准很简单:如果用户不处理这个框就不该继续操作页面,用 dialog;如果只是顺带看一眼,用 popover。
总结一下:<dialog> + showModal() 是当前做模态框的最短路径,method="dialog" 负责回传结果,::backdrop 管遮罩,动画记得补 allow-discrete 和 @starting-style,重复调用和 display 覆盖是两个最容易踩的坑。
原文链接:https://www.gj0.com/thread-1200.html
转载请注明出处并保留本声明;内容仅代表作者观点,与本站立场无关。若本文涉嫌侵权,请联系本站处理。