监听浏览器切屏功能实现

4,053 阅读2分钟

前言

由于在公司大部分时间都是在做考试系统,监听用户在考试期间的切屏操作并上报是比较常见的需求,本文主要是是实现这个需求并做个总结,下面就是我当初实现此需求的思路历程,希望能够帮到各位。

文中的代码片段在后面可以直接在线预览

第一版实现需求

经过在网上搜寻一堆资料,首先我们可以先看到 visibilitychange 这个 API,在 MDN 中给它的定义是:当其选项卡的内容变得可见或被隐藏时,会在文档上触发 **visibilitychange**(能见度变更)事件。
划重点❗ :选项卡
仔细一想,欸!这不就是我们想要的功能,下面就开始愉快的敲代码吧。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <script>
    let pageSwitchRecord = [];
		let ul = document.createElement('ul');

    document.addEventListener('visibilitychange', function () {
      if (document.hidden) {
        document.title = '用户切屏啦';
    
        let record = {
          time: new Date().getTime(),
          type: 'leave'
        };
    
        // 这里可以根据自己项目的需求进行自定义操作,例如上报后台、提示用户等等
    
        let li = document.createElement('li');
        li.className = 'leave'
        li.innerText = `用户在${record.time}切走了`;
        ul.appendChild(li);
    
        pageSwitchRecord.push(record);
      } else {
        document.title = '用户回来啦';
    
        let record = {
          time: new Date().getTime(),
          type: 'enter'
        };
    
        // 这里可以根据自己项目的需求进行自定义操作
    
        let li = document.createElement('li');
        li.className = 'enter'
        li.innerText = `用户在${record.time}回来了,耗时${record.time - pageSwitchRecord[pageSwitchRecord.length - 1].time}ms`;
        ul.appendChild(li);
    
        pageSwitchRecord.push(record);
      }
      document.body.appendChild(ul);
    });
  </script>
  <body></body>
</html>

以上就是根据 visibitychange 完成的第一版简易监听浏览器切屏功能。
就是在自测过程我们就能发现这方法也不能监听所有的浏览器切屏事件啊,就像下面两种情况

  • 直接使用 ALT+TAB 键切换不同的应用时并不会触发上面的方法;
  • 打开浏览器调试面板后,在调试面板中进行任意操作也是不会触发上的方法。

这里就要回到上面👆划的重点——选项卡,也就是说这个 API 只能监听到浏览器标签页的可见状态是否发生变化,当整个浏览器切入后台时也并不会触发,当然在标签页的调试面板里的任意操作可不会监听到,因为本质上标签页的可见状态并没有发上变化。
使用 visibilitychange 时需要注意的点❗ :

  • 微信内置的浏览器因为没有标签,所以不会触发该事件
  • 手机端直接回到桌面,也不会触发该事件
  • PC端浏览器失去焦点不会触发该事件,但是最小化或回到桌面会触发

第二版实现需求

这一版的实现就是我目前项目中使用的方案,当元素得到焦点和失去焦点都会触发 focusblur 事件,那么可不可以直接给 window 加上这两个事件的监听器呢?话不多说,直接开始试试吧。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <script>
    let pageSwitchRecord = [];
    let ul = document.createElement('ul');

    const leave = () => {
      document.title = '用户切屏啦';

      let record = {
        time: new Date().getTime(),
        type: 'leave'
      };

      // 这里可以根据自己项目的需求进行自定义操作,例如上报后台、提示用户等等

      let li = document.createElement('li');
      li.className = 'leave';
      li.innerText = `用户在${record.time}切走了`;
      ul.appendChild(li);

      pageSwitchRecord.push(record);

      document.body.appendChild(ul);
    };

    const enter = () => {
      document.title = '用户回来啦';

      let record = {
        time: new Date().getTime(),
        type: 'enter'
      };

      // 这里可以根据自己项目的需求进行自定义操作

      let li = document.createElement('li');
      li.className = 'enter';
      li.innerText = `用户在${record.time}回来了,耗时${
        record.time - pageSwitchRecord[pageSwitchRecord.length - 1].time
      }ms`;
      ul.appendChild(li);

      pageSwitchRecord.push(record);
      document.body.appendChild(ul);
    };

    window.addEventListener('blur', leave);
    window.addEventListener('focus', enter);
  </script>
  <body></body>
</html>

上面就是第二版实现需求的完整代码,可以看到处理用户切屏的逻辑都是一样的,区别在于监听浏览器切屏的方法,第二种采用的是监听 blurfocus 这两个事件去相互配合实现的。

预览

第一种方案

第二种方案

补充

  • 第二种相较于第一种实现方式有更加灵敏的监听,但是有可能在部分使用场景下会误触,为了保持准确性可以第一种和第二种方案配合使用
  • 使用 visibilitychange 时,为了保证兼容性,请使用 document.addEventListener 来注册回调,官方文档戳我

总结

回顾一下实现这个需求学到的知识点:

  • visibilitychange:浏览器标签页显隐时触发的事件
  • blur 事件:元素失去焦点事件
  • focus 事件 :元素获得焦点事件
  • addEventListener:给元素添加事件监听器
  • removeEventListener:移除元素上的事件监听器

参考资料

visibilitychange 官方文档
掘金文章《visibilitychange 处理页面关闭事件》

结语

感谢各位能看到这里!如果有所收获就点个赞再走吧,如果都更简便的实现方式或是有什么缺陷欢迎在评论区交流。