现代浏览器禁用自定义提示文字且不感知表单修改,必须手动跟踪isdirty状态:监听input事件设标志、提交后重置,并在beforeunload中同步调用e.preventdefault()兜底提醒。

onbeforeunload 在现代浏览器中无法可靠触发“刷新时提示保存”,必须配合手动状态跟踪,且不能依赖返回字符串。
为什么直接写 onbeforeunload="return '请保存'" 不起作用
Chrome、Firefox、Edge 从 2019 年起已禁用自定义提示文字,只显示统一文案:“离开此页面?您输入的数据可能未保存。”——你 return 的字符串会被忽略,甚至在某些版本里触发控制台警告 beforeunload handler returned a value。更关键的是:onbeforeunload 本身不感知表单是否修改,它只管“要走了”,不管“有没有改过”。
- 没做任何输入就刷新,也会弹(如果绑了但没判状态)
- 输完立刻提交,再刷新仍弹(没重置状态)
- 用
fetch或localStorage.setItem在事件里异步存数据?大概率被浏览器中断,不执行
必须手动监听 input/change 并设 isDirty 标志
核心逻辑是:页面加载时设 let isDirty = false,所有可编辑控件(<input>、<textarea></textarea>、<select></select>)绑定 input 事件(不是 change,后者需失焦),触发时设 isDirty = true;表单成功提交后,立刻设 isDirty = false。
-
input比change更及时,支持实时打字、粘贴、快捷键删改 - 富文本编辑器(如 UEditor、TinyMCE)需单独监听其 API 事件,例如
editor.on('contentChange', () => { isDirty = true }) - 避免重复绑定:SPA 路由切换时,先
removeEventListener('beforeunload', handler),再重新绑定
正确绑定 beforeunload 的唯一写法
只有一种方式能稳定生效:同步调用 e.preventDefault(),且必须在事件回调内直接执行,不能包在 Promise、setTimeout 或 fetch 回调里。
window.addEventListener('beforeunload', (e) => {
if (isDirty) {
e.preventDefault();
}
});
- 不要写
e.returnValue = 'xxx',它已废弃,部分浏览器报错 - 不要写
return false或return 'xxx',现代环境无效 - Safari 要求页面至少有一次用户交互(比如点过输入框)才会响应
beforeunload,纯静态页直接忽略
移动端和 SPA 场景下的现实限制
在 iOS Safari 和多数安卓浏览器中,beforeunload 基本不可靠:切到后台、锁屏、从任务管理器杀进程,事件几乎不触发。此时仅靠它“提醒”远远不够,必须降级为“实时暂存”。
- 对每个字段加防抖
setTimeout(如 800ms),避免高频写localStorage - 只存变更值(对比旧值再写),减少 I/O 开销
- 监听
paste事件,防止粘贴内容漏判 - 用户点击“提交”按钮后,清空
localStorage对应 key,避免下次打开误恢复草稿
真正容易被忽略的点是:很多人以为绑了 beforeunload 就万事大吉,其实它只是最后一道弱防线;真正防丢数据的,是 input 时刻存、blur 时刻存、submit 成功后清理——beforeunload 只负责兜底提醒,且只在桌面端部分场景下有效。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











