原生 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"并手动聚焦,保证键盘可操作 - 点击“确定”时 resolve(
true),点击“取消”或按Esc键 resolve(false) - 务必在 resolve 后调用
remove()清理 DOM,否则重复调用会堆叠多个弹窗
示例关键逻辑:
function showConfirm(message) {
return new Promise(resolve => {
const overlay = document.createElement('div');
overlay.innerHTML = `
<div class="confirm-modal">
<p>${message}</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill6712" title="Wechat HTML Publisher"><img
src="https://img.php.cn/upload/skill/000/000/081/179109368394970.jpg" alt="Wechat HTML Publisher" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill6712" title="Wechat HTML Publisher" class="overflowclass">Wechat HTML Publisher</a>
<p class="overflowclass">直接上传HTML富文本到微信公众号草稿箱。支持完整的HTML格式,无需Markdown转换。</p>
</div>
<a rel="nofollow" href="/xiazai/skill6712" title="Wechat HTML Publisher" 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 函数:
async function handleClick() { const ok = await showConfirm(...); } - Vue/React 中注意不要在渲染函数里 await,会破坏响应式或触发 warning
- 旧版 Safari 对
Promise构造函数内 DOM 操作有微小延迟,建议加setTimeout(resolve, 0)包一层(仅极少数场景需要) - 如果项目用了 Shadow DOM,弹窗要 append 到
document.body,而不是组件根节点,否则可能被隔离
要不要用第三方库?比如 dialog 元素或 AlertifyJS
<dialog></dialog> 语义清晰、自带 showModal(),但 IE 完全不支持,Safari 直到 15.4 才稳定支持 backdrop,且无法自定义按钮文案或插入复杂内容;AlertifyJS 这类老库依赖 jQuery,体积大,API 设计过时。
- 纯现代项目(Chrome/Firefox/Safari ≥16)可试
<dialog></dialog>,但得写 fallback(比如检测'showModal' in HTMLDialogElement.prototype) - 业务逻辑简单、只需文本确认 → 自己写上面那段 20 行 DOM 逻辑最稳
- 需要加载状态、多步骤、国际化 → 别硬撑,上
react-modal或@headlessui/react这类轻量可控的 UI 库
真正难的从来不是“怎么弹出来”,而是“用户点了确定之后,网络失败了怎么办”——那个部分才该花时间设计重试、防重复提交和 UI 反馈。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










