如何判断用户是否离开了当前页面?

在前端开发中,“用户离开当前页面” 包含多种场景:切换标签页、最小化浏览器、关闭标签页 / 浏览器、跳转到其他页面、电脑休眠 / 锁屏等。针对不同场景,我们可以通过浏览器提供的 API 进行精准检测,以下是详细的实现方案和场景适配。如何判断用户是否离开了当前页面?

一、核心 API 分类及适用场景

1. Page Visibility API(页面可见性检测)

这是最常用、最精准的检测方案,专门用于判断页面是否处于 “可见状态”,能区分「切换标签页、最小化浏览器、锁屏」等场景。

核心原理

浏览器提供 
document.hidden
 布尔值(
true
 表示页面不可见,
false
 表示可见),并通过 
visibilitychange
 事件触发状态变更。

代码实现


// 监听页面可见性变化
document.addEventListener('visibilitychange', function() {
  if (document.hidden) {
    // 页面变为不可见(切换标签、最小化、锁屏等)
    console.log('用户离开了当前页面(不可见)');
    // 执行业务逻辑:暂停视频播放、暂停定时器、上报用户离开行为等
    handlePageLeave();
  } else {
    // 页面恢复可见(用户切回标签)
    console.log('用户回到了当前页面');
    handlePageResume();
  }
});
 
// 离开页面的处理逻辑
function handlePageLeave() {
  // 示例:暂停视频
  const video = document.querySelector('video');
  if (video) video.pause();
  // 示例:上报用户离开行为
  fetch('/api/user/leave', { method: 'POST' });
}
 
// 回到页面的处理逻辑
function handlePageResume() {
  // 示例:恢复视频播放
  const video = document.querySelector('video');
  if (video) video.play();
}
关键说明

触发场景:✅ 切换到其他标签页✅ 最小化浏览器窗口✅ 电脑锁屏 / 休眠✅ 切换到浏览器的其他窗口❌ 关闭标签页 / 浏览器(部分浏览器可能触发,但不可靠)❌ 跳转到其他页面(不会触发,因为页面已卸载)兼容性:支持 IE10+、所有现代浏览器(Chrome、Firefox、Safari 等),是首选方案。扩展属性

document.visibilityState
:返回更详细的状态,可选值:

visible
:页面可见(前台标签)
hidden
:页面不可见(后台标签、最小化、锁屏)
prerender
:页面预渲染(不可见)
unloaded
:页面即将卸载(部分浏览器支持)

2. beforeunload 事件(页面卸载前检测)

用于检测「用户关闭标签页、关闭浏览器、跳转到其他页面、刷新页面」等会导致页面卸载的场景。

核心原理

当页面即将被卸载时触发 
beforeunload
 事件,可在此时执行收尾操作(如保存数据、上报行为),但浏览器对该事件的限制较多(防止恶意弹窗)。

代码实现


// 监听页面卸载前事件
window.addEventListener('beforeunload', function(e) {
  // 1. 执行必要的收尾操作(同步操作,异步操作可能被浏览器中断)
  console.log('用户即将离开页面(卸载)');
  saveUserProgress(); // 同步保存用户进度
 
  // 2. (可选)提示用户是否确认离开(部分浏览器限制弹窗样式)
  e.preventDefault(); // 标准做法,部分浏览器需要
  e.returnValue = ''; // 必须设置,否则弹窗不生效(Chrome/Firefox)
  return ''; // 兼容旧版浏览器
});
 
// 同步保存用户进度(异步操作可能失效)
function saveUserProgress() {
  // 示例:使用 localStorage 同步保存(不要用 fetch/axios 异步请求)
  localStorage.setItem('lastProgress', JSON.stringify({ time: Date.now() }));
}
关键说明

触发场景:✅ 关闭标签页 / 浏览器✅ 点击链接跳转到其他页面✅ 刷新页面✅ 在地址栏输入新 URL 并访问❌ 切换标签页 / 最小化浏览器(不会触发,页面未卸载)重要限制
事件处理函数中,异步操作(如 fetch、setTimeout)可能被浏览器中断,因此只能执行同步操作(如 localStorage、sessionStorage)。浏览器对弹窗提示的样式和文案有严格限制,无法自定义文案(统一显示浏览器默认提示),且部分浏览器(如 Safari)可能完全禁用弹窗。不要滥用该事件,频繁触发会影响用户体验,浏览器可能限制其执行。

3. unload 事件(页面卸载完成检测)


unload
 事件在页面完全卸载时触发,比 
beforeunload
 晚,几乎无法执行有效操作,仅用于兜底检测。

代码实现


window.addEventListener('unload', function() {
  console.log('页面已卸载');
  // 注意:此处异步操作几乎都会被浏览器取消,同步操作也可能失效
  // 仅建议做极简单的同步操作,不依赖执行结果
});
关键说明

触发场景:与 
beforeunload
 一致,但执行时机更晚。限制:浏览器会优先终止页面资源,因此该事件中的代码执行极不可靠,不建议依赖此事件处理核心业务

4. focus/blur 事件(窗口焦点检测)

通过监听浏览器窗口的焦点变化,辅助判断用户是否离开页面(兼容性好,但精准度低于 Page Visibility API)。

代码实现


// 窗口失去焦点(用户切换到其他窗口/应用)
window.addEventListener('blur', function() {
  console.log('窗口失去焦点(用户可能离开)');
});
 
// 窗口获得焦点(用户切回当前窗口)
window.addEventListener('focus', function() {
  console.log('窗口获得焦点(用户返回)');
});
关键说明

触发场景:✅ 切换到其他应用(如微信、Excel)✅ 切换到浏览器的其他窗口✅ 点击浏览器地址栏 / 书签栏❌ 切换到同一浏览器的其他标签页(部分浏览器不触发,如 Chrome)缺点:精准度低,无法区分 “切换到其他标签页” 和 “切换到其他应用”,仅作为辅助方案。

二、组合方案:覆盖所有场景

为了全面检测用户 “离开页面” 的所有场景,建议结合 Page Visibility API 和 beforeunload 事件:



// 方案1:检测页面可见性变化(切换标签、最小化、锁屏)
document.addEventListener('visibilitychange', function() {
  if (document.hidden) {
    console.log('用户离开页面(不可见)');
    handlePageInactive(); // 处理页面不可见逻辑
  } else {
    console.log('用户返回页面(可见)');
    handlePageActive(); // 处理页面可见逻辑
  }
});
 
// 方案2:检测页面卸载(关闭标签、跳转、刷新)
window.addEventListener('beforeunload', function(e) {
  console.log('用户即将卸载页面');
  handlePageUnload(); // 处理页面卸载前逻辑
  e.preventDefault();
  e.returnValue = '';
  return '';
});
 
// 页面不可见时的处理逻辑
function handlePageInactive() {
  // 暂停视频、定时器、动画等
  pauseMedia();
  pauseTimers();
  // 上报用户离开行为(异步请求,此处可执行,因为页面未卸载)
  fetch('/api/user/inactive', { method: 'POST' });
}
 
// 页面可见时的处理逻辑
function handlePageActive() {
  // 恢复视频、定时器、动画等
  resumeMedia();
  resumeTimers();
}
 
// 页面卸载前的处理逻辑
function handlePageUnload() {
  // 同步保存用户数据(异步请求可能失效)
  saveUserStateSync();
}
 
// 示例:暂停/恢复视频
function pauseMedia() {
  const media = document.querySelectorAll('video, audio');
  media.forEach(el => el.pause());
}
function resumeMedia() {
  const media = document.querySelectorAll('video, audio');
  media.forEach(el => el.play().catch(err => console.log('自动播放被阻止', err)));
}
 
// 示例:暂停/恢复定时器(需存储定时器ID)
let timerId = setInterval(() => console.log('定时器运行'), 1000);
function pauseTimers() {
  clearInterval(timerId);
}
function resumeTimers() {
  timerId = setInterval(() => console.log('定时器运行'), 1000);
}
 
// 示例:同步保存用户状态
function saveUserStateSync() {
  const state = {
    lastAction: Date.now(),
    page: window.location.href
  };
  localStorage.setItem('userState', JSON.stringify(state));
}

三、常见问题与解决方案

问题 1:beforeunload 中的异步请求失效

原因:浏览器在页面卸载时会中断异步操作,优先释放资源。解决方案:
使用 
navigator.sendBeacon()
:这是浏览器提供的专门用于页面卸载时发送异步请求的 API,能保证请求被发送(即使页面已卸载)。



window.addEventListener('beforeunload', function() {
  // sendBeacon 发送异步请求,浏览器会保证其完成
  const data = new FormData();
  data.append('action', 'page_unload');
  data.append('time', Date.now().toString());
  navigator.sendBeacon('/api/user/unload', data);
});

关键限制:
navigator.sendBeacon
 仅支持 POST 请求,且数据格式为 FormData/Blob/URLSearchParams,无法设置自定义请求头(如 Token),需通过参数传递鉴权信息。

问题 2:区分 “关闭标签页” 和 “刷新页面”

难点:浏览器未提供直接区分的 API,需通过间接方式判断。解决方案:
利用 
sessionStorage
 记录页面加载状态:



// 页面加载时
window.addEventListener('load', function() {
  if (sessionStorage.getItem('isRefreshing')) {
    console.log('用户刷新了页面');
    sessionStorage.removeItem('isRefreshing');
  } else {
    console.log('用户首次打开页面');
  }
});
 
// beforeunload 时标记为刷新
window.addEventListener('beforeunload', function() {
  // 检测是否是刷新操作(通过鼠标事件/键盘事件辅助判断)
  let isRefresh = false;
  // 监听 F5 刷新键
  window.addEventListener('keydown', function(e) {
    if (e.key === 'F5' || (e.ctrlKey && e.key === 'r')) {
      isRefresh = true;
    }
  });
  // 监听浏览器刷新按钮点击(无法直接监听,需结合其他逻辑)
  if (isRefresh) {
    sessionStorage.setItem('isRefreshing', 'true');
  }
});

说明:该方案仅能部分区分,无法 100% 精准判断(如用户点击浏览器刷新按钮无法直接检测),建议仅作为辅助逻辑。

问题 3:移动端适配

移动端的 “离开页面” 场景:切换到其他 App、锁屏、返回桌面、关闭浏览器标签。适配方案:
Page Visibility API 同样适用于移动端浏览器(如 Chrome 移动端、Safari 移动端)。移动端的 
beforeunload
 事件可能被浏览器限制(如 Safari 移动端禁用弹窗提示),建议优先使用 
navigator.sendBeacon()
 上报数据。

问题 4:浏览器兼容性

API / 事件 兼容性 备注
Page Visibility API IE10+、Chrome 13+、Firefox 10+、Safari 7+ 移动端浏览器(Chrome/Safari)均支持
beforeunload IE4+、所有现代浏览器 移动端 Safari 对弹窗提示有限制
navigator.sendBeacon Chrome 39+、Firefox 31+、Safari 11.1+ 移动端 Chrome 支持,iOS Safari 11.1+ 支持

四、业务场景适配建议

业务场景 推荐方案
暂停 / 恢复视频、音频播放 Page Visibility API
暂停 / 恢复定时器、动画 Page Visibility API
上报用户 “离开页面” 行为(切换标签 / 最小化) Page Visibility API + fetch 异步请求
上报用户 “关闭页面” 行为(关闭标签 / 跳转) beforeunload + navigator.sendBeacon
保存用户进度 / 数据 beforeunload + localStorage/sessionStorage 同步保存
提示用户 “是否确认离开”(如未保存表单) beforeunload 事件(注意浏览器弹窗限制)

五、总结

判断用户是否离开当前页面,核心是区分「页面不可见」和「页面卸载」两类场景:

「页面不可见」(切换标签、最小化、锁屏):优先使用 Page Visibility API,精准且无副作用。「页面卸载」(关闭标签、跳转、刷新):使用 beforeunload 事件,结合 
navigator.sendBeacon
 保证异步请求生效,同步操作存储关键数据。辅助方案:focus/blur 事件可作为补充,但精准度较低,不建议单独使用。

实际开发中,需结合业务需求选择合适的方案,同时注意浏览器的限制(如异步请求、弹窗提示),确保功能稳定且不影响用户体验。

© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
none
暂无评论...