Esc键默认不触发cancel回调,因layui的esc:true仅调用layer.close(index)绕过cancel生命周期;需在success中绑定keyup事件手动调cancel再close,并于end中解绑防泄漏。
esc 键默认会关闭弹层,但 cancel 回调不会自动触发——这是最常被忽略的逻辑断点。
为什么按 Esc 不执行 cancel 回调
layui 的 esc: true(默认开启)只是让按下 Esc 后调用 layer.close(index),这个过程绕过了 cancel 生命周期。它只等同于用户点了右上角 × 或点击遮罩(当 shadeClose: true 时),但不等同于“用户主动取消”。cancel 是语义化回调,用于清理状态、还原表单、中断请求等,Esc 默认不走这条路。
-
cancel只在点击关闭按钮、点击遮罩(shadeClose: true)、或显式调用layer.close(index)且该层配置了cancel时才触发 - 全局
layer.closeAll()会跳过所有cancel和end,绝对不要在 Esc 监听里用它 - iframe 类型弹窗中,若焦点在子页面内,主页面的
document.keyup根本收不到事件,不是 bug,是浏览器限制
在 success 中绑定 keyup 并手动调 cancel
必须把 cancel 函数提前存为变量,在 success 里监听 document 的 keyup,命中 e.which === 27 时先调 cancel(index, layero),再调 layer.close(index)。
- 绑定时加命名空间(如
'keyup.esc-'+index),避免冲突和重复绑定 - 一定要在
end回调里用$(document).off('keyup.esc-'+index)解绑,否则内存泄漏 - 别用
e.key === 'Escape'—— 旧版 IE 不支持,统一用e.which === 27 - 如果
cancel返回false,表示阻止关闭,则后续layer.close(index)就不该执行
iframe 弹窗里 Esc 失效怎么办
type: 2 的弹窗加载外部页面后,键盘事件发生在 iframe 内部文档,主页面监听不到。解决路径只有一条:从 iframe 内部出发。
- 若 iframe 页面可控(同源),在其中加:
document.addEventListener('keydown', e => e.which === 27 && parent.layer.close(parent.layer.getframeindex(window.name))) - 若跨域,无法注入脚本,
parent.layer不可达,此时 Esc 支持必须放弃,改用显式按钮或组合键(如 Ctrl+Q) - 别尝试用
iframe.contentWindow.postMessage主动发消息——跨域下Permission denied会直接报错
慎用全局 document.keydown 绑定
有人写 $(document).keydown(e => e.which === 27 && layer.closeAll()),看着省事,实际隐患极大。
- 用户正在输入表单,误按 Esc,未保存数据直接清空所有弹层
-
layer.closeAll()会干掉 loading、tips、toast 等非 dialog 类型层,UI 状态瞬间混乱 - 多层嵌套时,它不区分层级关系,父层可能比子层更早销毁,导致回调执行顺序错乱
- 真正安全的做法始终是:精确到
index,只关当前层,生命周期可控
真正难的不是绑定事件,而是判断「此刻用户是否真的想放弃」——比如表单已修改但未提交,cancel 里要做防丢失提示,而这个逻辑一旦和 Esc 绑定,就必须同步覆盖所有退出路径(按钮、遮罩、Esc),否则体验割裂。











