网页中如何判断用户是否处于闲置状态

最近在项目中遇到一个需求:需要监控用户是否开着页面但是没有操作,或者开了页面但已经离开电脑,发现了浏览器原生提供了专有的 API: IdleDetector(空闲检测器)。当然我们也可以自行实现,我们分别来聊聊这两种方式:

什么是 IdleDetector?

IdleDetector 是浏览器提供的一个实验性 API,用于检测用户的闲置状态。它可以监控以下两种状态:

  • 用户状态(user):用户是否在电脑前(通过键盘、鼠标等交互判断)。
  • 屏幕状态(screen):屏幕是否被锁定(例如屏幕保护程序启动或电脑休眠)。

目前,IdleDetectorChrome 94+Edge 94+ 中都支持,FirefoxSafari 尚未支持。但是 IdleDetector 仍为实验性 API,未来可能有变化,且在非 HTTPS 环境下无法使用。

底层原理

IdleDetector 通过浏览器底层机制检测用户和屏幕状态,开发者只需设置阈值并监听状态变化即可。API 会根据用户交互和系统状态自动判断是否进入闲置。

使用要注意

IdleDetector 需要用户授权,权限请求可通过以下两种方式触发:

  • 显式请求 :调用 IdleDetector.requestPermission(),需在用户手势(如点击)中触发。
  • 隐式请求 :直接调用 detector.start(),浏览器会自动弹出权限提示。

如果正确被调用会有这样的提示:

参考以下实现来调用:

js 复制代码
async function startIdleDetection(threshold = 60000) {
  if (!window.IdleDetector) {
    console.error('浏览器不支持 IdleDetector API');
    return { success: false, error: 'API_NOT_SUPPORTED' };
  }
  try {
    // 至少要60秒, 任何小于 60 秒的设置会导致 start() 抛出 DOMException
    const validatedThreshold = Math.max(threshold, 60000);
    const state = await IdleDetector.requestPermission();
    if (state !== 'granted') {
      console.error('用户拒绝了空闲检测权限');
      return { success: false, error: 'PERMISSION_DENIED' };
    }
    const detector = new IdleDetector();
    // 添加更健壮的事件处理
    const handleStateChange = () => {
      const now = new Date().toISOString();
      console.log(`[${now}] 用户状态: ${detector.userState}, 屏幕状态: ${detector.screenState}`);
      // 实际业务逻辑可以在这里添加
      if (detector.userState === 'idle') {
        // 用户闲置处理
      }
    };
    detector.addEventListener('change', handleStateChange);
    // 添加错误处理
    detector.addEventListener('error', (err) => {
      console.error('检测器错误:', err);
    });
    await detector.start({ threshold: validatedThreshold });
    return { success: true, detector }; // 返回检测器实例以便后续控制
  } catch (err) {
    console.error('空闲检测初始化失败:', err);
    return { success: false, error: err.message };
  }
}

// 正确用法示例(必须在用户交互中调用):
document.getElementById('startBtn').addEventListener('click', async () => {
  const result = await startIdleDetection(90000); // 90秒阈值
  if (!result.success) {
    // 处理失败情况
  }
});

如果用户拒绝了怎么办了?我们要有备选方案:

自行实现用户闲置检测

通过监听一系列浏览器事件,判断用户是否处于活跃状态。核心事件包括:

  • visibilitychange:检测页面是否可见(例如用户切换标签页或最小化窗口)。
  • mousemove / mousedown / keydown / touchstart等事件:检测用户是否与页面交互。
  • focus / blur:检测窗口是否获得焦点。

通过设置一个定时器,当用户超过一定时间未触发上述事件时,判定为闲置状态。

示例代码:

javascript 复制代码
class IdleMonitor {
  constructor(idleTimeout = 60000) {
    this.idleTimeout = idleTimeout; // 闲置超时时间(毫秒)
    this.timer = null;
    this.isIdle = false;

    this.handleActivity = this.handleActivity.bind(this);
    this.checkIdle = this.checkIdle.bind(this);

    this.init();
  }

  init() {
    // 监听用户交互事件
    ['mousemove', 'mousedown', 'keydown', 'touchstart'].forEach(event => {
      window.addEventListener(event, this.handleActivity);
    });

    // 监听页面可见性
    document.addEventListener('visibilitychange', this.handleActivity);

    // 启动定时器
    this.startTimer();
  }

  handleActivity() {
    if (this.isIdle) {
      this.isIdle = false;
      console.log('用户恢复活跃');
    }
    this.startTimer();
  }

  startTimer() {
    clearTimeout(this.timer);
    this.timer = setTimeout(this.checkIdle, this.idleTimeout);
  }

  checkIdle() {
    this.isIdle = true;
    console.log('用户进入闲置状态');
  }

  destroy() {
    clearTimeout(this.timer);
    ['mousemove', 'mousedown', 'keydown', 'touchstart'].forEach(event => {
      window.removeEventListener(event, this.handleActivity);
    });
    document.removeEventListener('visibilitychange', this.handleActivity);
  }
}

// 使用示例
const monitor = new IdleMonitor(90000); // 90秒无操作进入闲置

这里只是举例,所以只用了点击、触摸、鼠标移动这些事件,实际上要监听的事件可能要更多一些,如 scrollwheeltouchmove,也可以根据业务适当调整。但是对比原生的IdleDetector有一个弊端就是跨域的 iframe 中的无法监听到。跨域的 iframe 内的交互事件(如 mousemovekeydown)不会冒泡到父页面,但如果 iframe 和父页面同源,可以在 iframe 内部绑定事件监听并通过 postMessage 通知父页面。实际使用的时候也可以通过防抖或节流来优化频繁触发的事件比如 mousemove 降低性能开销。

总结

浏览器为开发者检测用户是否空闲提供了实验性的API: IdleDetector,但是需要比较新的浏览器并且需要用户显式授权,如果浏览器不支持或者用户拒绝了,我们也可以通过自行实现的方式来检测,只是有些许缺陷。如果你也有类似的诉求,希望这篇帖子可以帮助到你。有用请点赞,喜欢请关注,我是 Senar, 是不是分享一些有趣的前端开发技巧,我们下期再见~

相关推荐
神仙别闹8 分钟前
基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
前端·后端·asp.net
步行cgn5 小时前
Vue 中的数据代理机制
前端·javascript·vue.js
GH小杨5 小时前
JS之Dom模型和Bom模型
前端·javascript·html
星月心城6 小时前
JS深入之从原型到原型链
前端·javascript
MessiGo6 小时前
Javascript 编程基础(5)面向对象 | 5.2、原型系统
开发语言·javascript·原型模式
你的人类朋友6 小时前
🤔Token 存储方案有哪些
前端·javascript·后端
烛阴6 小时前
从零开始:使用Node.js和Cheerio进行轻量级网页数据提取
前端·javascript·后端
liuyang___6 小时前
日期的数据格式转换
前端·后端·学习·node.js·node
西哥写代码6 小时前
基于cornerstone3D的dicom影像浏览器 第三十一章 从PACS服务加载图像
javascript·pacs·dicom
贩卖纯净水.7 小时前
webpack其余配置
前端·webpack·node.js