🔍 如何判断用户是否离开了当前页面
在现代 Web 开发中,我们常常需要识别用户是否还停留在当前页面。这个需求涉及用户体验、数据分析与系统性能等多个重要方面。
“离开页面”可以拆解为以下几种场景:
- 切换到其他标签页或应用(页面不可见,但未关闭)。
- 最小化浏览器窗口。
- 关闭标签页或整个浏览器。
- 当前标签页中导航到新 URL。
- 在移动设备上切换 App 或返回主屏幕。
前端提供了多种 API 来覆盖这些场景,下面逐一分析:
✅ 方法一:Page Visibility API
用途:检测页面是否对用户可见,适用于标签切换、最小化等场景。
核心属性:
document.hidden
document.visibilityState
visibilitychange
事件
使用场景示例:暂停视频、停止轮播、节流轮询请求等。
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
console.log('页面不可见 - 用户离开或切换标签');
pauseVideo();
} else {
console.log('页面可见 - 用户回来了');
playVideo();
}
});
优点:
- 标准 API,兼容主流浏览器
- 高性能、简单明确
- 适合节省资源与用户体验优化
局限:
- 无法区分关闭页面与切换标签
🚨 方法二:beforeunload
与 unload
事件
这些事件用于捕捉用户即将离开的行为,但存在兼容性和用户体验方面的问题:
beforeunload
- 在页面卸载前触发
- 可阻止离开(通过
event.returnValue
显示确认对话框) - 现代浏览器仅允许显示标准提示([dev.to][1])
unload
- 页面资源卸载时触发
- 不支持执行异步操作,且不可靠
适用场景:
beforeunload
:用于检测用户是否丢失未保存内容unload
:仅执行非常简单的同步清理任务
🚀 方法三:navigator.sendBeacon()
+ 推荐页面事件
为了解决 unload 中异步请求被中断问题,引入了 navigator.sendBeacon()
:
- 异步发送少量数据,即使页面卸载也能保证请求 可靠送达 ([developer.mozilla.org][2])
- 常在
visibilitychange
或pagehide
时调用:
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
navigator.sendBeacon('/log', analyticsData);
}
});
- 推荐替换
beforeunload
和unload
,与Page Visibility API
结合使用([developer.mozilla.org][2], [web.dev][3])
🧭 方法四:pagehide
/ pageshow
(针对 bfcache)
为了优化“后退缓存”(bfcache)机制,pagehide
提供更可靠的回调:
- 触发于导航离开或进入 bfcache
event.persisted
表示是否存入 bfcache- 同样适合发送 Beacon 注销数据
window.addEventListener('pagehide', event => {
navigator.sendBeacon('/log', analyticsData);
});
- 适配现代浏览器(包括移动设备),比
unload
更稳定([developer.mozilla.org][2])
🧩 新兴趋势:Pending Beacon / fetchLater API
浏览器正在探索更可靠的 Unload Beacon 方法:
- Pending Beacon:设计用于自动退载页面时发送,开发者无需监听事件([nicj.net][4])
- fetchLater():做为 Beacon 替代方案,允许设置延时或在页面卸载时发送([nicj.net][5])
📌 最佳实战建议
场景 | 推荐方案 |
---|---|
检测用户不再可见页面 | Page Visibility API + visibilitychange |
发送离开日志或分析数据 | navigator.sendBeacon() + visibilitychange 或 pagehide |
防止用户误关闭造成数据丢失 | beforeunload (有必要时使用,提示用户确认) |
替代传统 unload | 不要使用异步操作,改用 Beacon 或同步清理 |
bfcache 支持 | 使用 pagehide + navigator.sendBeacon() |
未来优化策略 | 留意 Pending Beacon 或 fetchLater() API |
✅ 综合示例代码
// 页可见性操作
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
pauseVideo();
navigator.sendBeacon('/log', JSON.stringify({ ts: Date.now(), event: 'hidden' }));
} else {
playVideo();
}
});
// 页面卸载时的日志
window.addEventListener('pagehide', event => {
navigator.sendBeacon('/log', JSON.stringify({ ts: Date.now(), event: 'unload' }));
});
// 用户可能因输入问题进行警告
let isDirty = false;
form.addEventListener('input', () => { isDirty = true; });
window.addEventListener('beforeunload', e => {
if (isDirty) {
e.returnValue = ''; // 显示确认对话框
}
});
⚡ 总结
- 使用 Page Visibility API 判断标签切换与最小化;
- 使用 navigator.sendBeacon + visibilitychange/pagehide 实现可靠上报;
- 必要情况下结合 beforeunload 提示保留未保存内容;
- 避免使用 deprecated 的
unload
; - 关注新标准如 Pending Beacon/API,为未来优化做准备。
通过协调这些现代 API,我们能打造出兼顾准确性、性能与用户体验的用户离开检测方案。