原生alert/confirm/prompt不响应esc键,因其为同步阻塞式api,非dom元素,不派发任何事件;仅支持esc触发cancel事件,且需showmodal()并可preventdefault()拦截。

原生 alert()、confirm()、prompt() 按 Esc 无法关闭,也监听不到 —— 它们根本不是 DOM 元素,更不响应键盘事件。真要支持 Esc 关闭,必须用 <dialog></dialog> 或手动实现。
为什么 alert 按 Esc 没反应
这些是同步阻塞式 API,JS 执行会暂停,浏览器不派发任何事件。Esc 键在它们运行期间被完全忽略,既不会中断,也不会触发回调。你写的 addEventListener('keydown', ...) 在 alert() 弹出时根本没机会执行。
- 常见错误现象:
window.addEventListener('keydown', e => e.key === 'Escape' && console.log('esc!'))在alert()显示时完全不输出 - 别试图给
alert加事件监听或样式 —— 它不可定制、不可访问、不可扩展 - 如果业务逻辑依赖“用户是否点了 Esc”,说明你本就不该用
alert,该换方案
<dialog></dialog> 的 Esc 行为怎么控制
<dialog></dialog> 默认按 Esc 就触发 cancel 事件(即使没加 modal 属性),且这个行为可拦截、可阻止,但不能完全禁用而不损可访问性。
- 想阻止 Esc 关闭:监听
cancel并调用e.preventDefault(),但需有明确理由(如表单未保存) - 想让 Esc 关闭后执行清理逻辑:监听
close事件(注意:cancel不会触发close,除非你手动调用dialog.close()) - 不支持
returnValue的 Safari(v17+ 仍不稳定):别依赖dialog.returnValue判断关闭原因,改用自定义 data 属性或状态变量 - 必须用
showModal():只用show()时 Esc 不生效,也不锁定焦点
手写弹窗如何安全支持 Esc
如果你用的是 <div class="modal"> 这类自定义结构,Esc 关闭必须自己监听并管理焦点,否则屏幕阅读器和键盘用户会卡住。
<ul>
<li>监听 <code>keydown 事件时,确保只在弹窗打开状态下生效(比如加个 isModalOpen 标志位)
triggerBtn.focus()
e.stopPropagation() 拦全局事件 —— 这会让键盘焦点丢失、Tab 键失效tabindex="-1" 到弹窗容器,并在打开时 dialogEl.focus(),否则键盘用户进不去兼容性兜底要注意什么
<dialog></dialog> 在 Safari 15.4 之前全不支持,旧 Edge 也不行。检测不通过时降级,不能简单隐藏/显示,否则焦点和遮罩逻辑就断了。
- 检测写法必须是:
'showModal' in HTMLDialogElement.prototype,不是typeof dialog.showModal === 'function' - 降级方案里,
body { overflow: hidden }要配对清除,否则页面滚动被锁死 - backdrop 点击关闭别用
stopPropagation(),而应判断e.target === backdropEl再关 —— 否则 SVG 子元素点击失效 - 所有关闭动作(Esc、点击 backdrop、点 X 按钮)最终都应走同一套清理函数,避免状态不一致
真正麻烦的从来不是“怎么让 Esc 关闭”,而是“关完之后焦点去哪、状态清没清、屏幕阅读器知不知道”。漏掉其中任意一环,对部分用户来说,弹窗就等于永远打不开、也永远关不掉。











