
本文介绍利用页面可见性 API 捕获用户导航离开或关闭页面前的最后修改,结合 sendBeacon() 可靠提交未保存的表单数据,无需依赖失焦事件或提交按钮。
本文介绍利用页面可见性 api 捕获用户导航离开或关闭页面前的最后修改,结合 `sendbeacon()` 可靠提交未保存的表单数据,无需依赖失焦事件或提交按钮。
在构建以用户体验为中心的表单应用(如工时填报页)时,仅依赖 change 或 blur 事件存在明显盲区:当用户在最后一个输入框完成编辑后直接关闭标签页、切换到其他应用或刷新页面,输入框既未失焦也未触发 change,导致最新数据丢失。
此时,更可靠的方案是监听页面可见性状态变化——即使用 visibilitychange 事件。该事件在浏览器标签页变为不可见时(如用户切换标签、最小化窗口、关闭页面)立即触发,且保证在页面卸载前执行,是捕获“最终修改”的理想时机。
以下是一个完整实践示例:
// 监听页面可见性变化
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
// 收集当前所有文本框的最新值(例如:按 name 或 class 选取)
const formData = new FormData();
document.querySelectorAll('input[type="text"][name^="hours"]').forEach(el => {
if (el.value.trim()) {
formData.append(el.name, el.value);
}
});
// 使用 sendBeacon 发送数据(推荐:异步、不阻塞卸载、兼容性好)
if ('sendBeacon' in navigator) {
const blob = new Blob([JSON.stringify(Object.fromEntries(formData))], {
type: 'application/json'
});
navigator.sendBeacon('/api/save-hours', blob);
} else {
// 降级方案:使用 fetch + keepalive(注意:Firefox 当前不支持 keepalive)
fetch('/api/save-hours', {
method: 'POST',
body: JSON.stringify(Object.fromEntries(formData)),
headers: { 'Content-Type': 'application/json' },
keepalive: true // 允许请求在页面卸载后继续发送
}).catch(() => {/* 忽略网络失败,避免中断卸载 */});
}
}
});⚠️ 注意事项:
-
sendBeacon()仅支持POST请求,且不返回响应,适合日志上报或轻量数据持久化; - 它自动异步执行,并能在页面销毁后继续传输,成功率显著高于普通 AJAX;
- 若需结构化数据,建议将表单序列化为 JSON 并封装为
Blob(如上所示),避免编码问题; - 不要在此处执行耗时同步操作(如
alert()或大量计算),否则可能阻塞页面卸载; - 为防重复提交,可在触发后设置标志位(如
window.__savedOnExit = true),并在后续逻辑中校验。
总结:相比监听 beforeunload(已被现代浏览器限制弹窗且不保证执行完成),visibilitychange + sendBeacon 是更现代、更可靠、更符合规范的数据兜底策略,尤其适用于无提交按钮的“自动保存”场景。

















