原生 window.confirm() 不够用,因其阻塞主线程、无法自定义样式/图标/HTML、移动端体验差,且不支持异步等待;需用 Promise + DOM 实现可 await 的确认框,注意调用上下文为 async 函数、兼容 Shadow DOM 及旧版 Safari。
window.confirm() 为什么不够用它阻塞主线程,页面卡死,没法改样式、加图标、支持 HTML 内容,移动端体验差,还不能异步等待用户操作——比如你点“确定”后想先发个请求再关闭,confirm() 根本做不到。
Promise + 普通 DOM 实现可 await 的确认框核心是把用户点击封装成 Promise,调用时能 await showConfirm('删除后不可恢复?'),返回 true 或 false。
div 作为遮罩层(position: fixed; z-index: 9999),避免穿透点击tabindex="-1" 并手动聚焦,保证键盘可操作true),点击“取消”或按 Esc 键 resolve(false)remove() 清理 DOM,否则重复调用会堆叠多个弹窗示例关键逻辑:
function showConfirm(message) { return new Promise(resolve => { const overlay = document.createElement('div'); overlay.innerHTML = ` <div class="confirm-modal"> <p>${message}</p><p><span>立即学习</span>“<a href="https://pan.quark.cn/s/cb6835dc7db1" style="text-decoration: underline !important; color: blue; font-weight: bolder;" rel="nofollow" target="_blank">前端免费学习笔记(深入)</a>”;</p><div class="aritcle_card flexRow"> <div class="artcardd flexRow"> <a class="aritcle_card_img" href="/ai/1863" title="Cleanup.pictures"><img src="https://img.php.cn/upload/ai_manual/000/000/000/175680400564100.jpg" alt="Cleanup.pictures" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a> <div class="aritcle_card_info flexColumn"> <a href="/ai/1863" title="Cleanup.pictures">Cleanup.pictures</a> <p>智能移除图片中的物体、文本、污迹、人物或任何不想要的东西</p> </div> <a href="/ai/1863" title="Cleanup.pictures" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a> </div> </div> <button data-action="confirm">确定</button> <button data-action="cancel">取消</button> </div> `; document.body.appendChild(overlay); const btnConfirm = overlay.querySelector('[data-action="confirm"]'); const btnCancel = overlay.querySelector('[data-action="cancel"]'); const handleConfirm = () => { resolve(true); overlay.remove(); }; const handleCancel = () => { resolve(false); overlay.remove(); }; btnConfirm.addEventListener('click', handleConfirm); btnCancel.addEventListener('click', handleCancel); overlay.addEventListener('keydown', e => e.key === 'Escape' && handleCancel()); // 点击遮罩层外部不关闭(更安全),如需支持则监听 overlay.click });}
showConfirm() 调用时容易忽略的兼容性细节不是所有环境都允许直接 await 顶层代码;如果在非 async 函数里调用,会报错 “SyntaxError: await is only valid in async function”。
async function handleClick() { const ok = await showConfirm(...); }
Promise 构造函数内 DOM 操作有微小延迟,建议加 setTimeout(resolve, 0) 包一层(仅极少数场景需要)document.body,而不是组件根节点,否则可能被隔离dialog 元素或 AlertifyJS
<dialog> 语义清晰、自带 showModal(),但 IE 完全不支持,Safari 直到 15.4 才稳定支持 backdrop,且无法自定义按钮文案或插入复杂内容;AlertifyJS 这类老库依赖 jQuery,体积大,API 设计过时。
<dialog>,但得写 fallback(比如检测 'showModal' in HTMLDialogElement.prototype)react-modal 或 @headlessui/react 这类轻量可控的 UI 库真正难的从来不是“怎么弹出来”,而是“用户点了确定之后,网络失败了怎么办”——那个部分才该花时间设计重试、防重复提交和 UI 反馈。