showCustomAlert()必须动态创建DOM节点并返回Promise,监听close事件清理、管理焦点与滚动,降级兼容旧浏览器,SSR环境需window守卫。

封装 showCustomAlert() 时必须动态创建 DOM
硬编码写死 <dialog></dialog> 或弹窗结构在 HTML 里,会导致多个调用互相污染(比如第二次调用覆盖第一次的 textContent,或关闭按钮只清掉最后一个)。动态创建才是安全前提。
常见错误现象:document.getElementById("alert-dialog").showModal() 多次调用后只显示一个、点关闭后其他实例仍残留、Vue/React 组件卸载后弹窗还挂在 body 上。
- 每次调用都用
document.createElement("div")新建遮罩层和内容区 - 用
appendChild()插入document.body,别塞进某个容器内部(否则可能被overflow: hidden截断) - 关闭逻辑必须清理自身节点:
el.parentNode.removeChild(el),不能只设display: none
showCustomAlert() 要支持 Promise 风格回调
原生 alert() 是同步阻塞的,但自定义版本必须异步——否则无法配合 async/await 流程,也违背现代 JS 执行模型。
使用场景:比如用户点击“删除”按钮后,先弹确认框,等用户点“确定”才发请求;若不返回 Promise,就得靠回调嵌套,代码难维护。
立即学习“前端免费学习笔记(深入)”;
- 函数末尾返回
new Promise(resolve => { /* 绑定确定按钮 click 事件,触发 resolve() */ }) - 不要在 resolve 前直接移除 DOM 节点——得等动画结束再删,否则用户看不见关闭过程
- 记得监听
animationend或用setTimeout延迟清理(若没加 CSS 动画)
避免 Safari 和 iOS 的 focus 与 backdrop 关闭陷阱
<dialog></dialog> 在 Safari 15.4+ 支持 showModal(),但点击遮罩(backdrop)默认不触发 click 事件,只发 close 事件。很多封装漏了这点,导致点背景关不掉。
性能影响:不手动管理焦点,键盘 Tab 会跳出弹窗;不加 inert 或 aria-hidden,屏幕阅读器仍可读取背景内容。
- 必须监听
dialogEl.addEventListener("close", () => { /* 清理逻辑 */ }),而非clickon backdrop - 打开时调用
dialogEl.showModal()后,立刻执行dialogEl.querySelector("button, input").focus() - 关闭前给
document.body加style.overflow = "hidden",关完恢复(注意别覆盖原有 overflow 值)
不兼容 IE 和旧 Safari 时的降级 fallback 方案
<dialog></dialog> 在 IE 完全不可用,Safari ≤ 9.0 也不支持。强行用 showModal() 会静默失败或报错 dialogEl.showModal is not a function。
可检测支持性:typeof HTMLDialogElement !== "undefined" && "showModal" in HTMLDialogElement.prototype。不支持就切到 div 模拟方案。
- 降级方案中,遮罩层必须设
position: fixed; top: 0; left: 0; width: 100%; height: 100%,且z-index显式高于页面所有元素 - 禁止用
window.open()或target="_blank"模拟——它打不开模态行为,用户能自由切标签页 - SSR 环境(如 Next.js、Nuxt)中首次渲染时
window不存在,需加if (typeof window !== "undefined")守卫
真实项目里最容易被忽略的,是弹窗关闭后的焦点回收——用户按 Esc 或点 backdrop 关闭后,焦点不该留在虚空里,得手动 document.body.focus() 或回到触发按钮上,否则键盘用户会迷失。



















