页面可见性改变事件 : visibilitychange 详解

1、Page Visibility API标准概述

查看W3C的官方文档时候看到这个属性 标准时间线是这样介绍的:

Page Visibility Level 2
W3C Proposed Recommendation 17 October 2017

查看W3C的工作流程的时候可以看到应该是处于W3C提议推荐的阶段,虽然还没有被完全的推荐成为标准,并且不是每一款浏览器都有所支持,但还是值得研究一下。

2、定义

顾名思义这是一个页面可见性API。

简单的说,浏览器标签页被隐藏或显示的时候会触发visibilitychange事件。

这是HTML5新提供的一个api,作用是记录当前标签页在浏览器中的激活状态。
所谓“激活状态”指当前标签是否正在被用户浏览。

我们知道,平时在PC端浏览网页的时候,使用的都是选项卡这种方式浏览网页,使用这种方式浏览,任何给定网页都有可能在后台,因此对用户不可见。页面可见性API提供了开发者可以观察的事件,以便了解文档何时可见或隐藏,以及查看页面当前可见性状态的功能。

页面可见性API对于节省资源和提到性能特别有用,它使页面在文档不可见时避免执行不必要的任务。

当用户最小化窗口或切换到另一个选项卡时,API会发送visibilitychange事件,让开发者知道页面状态已更改。你可以检测事件并执行某些操作或行为。例如,如果你的网络应用正在播放视频,则可以在用户将标签放入背景时暂停视频,并在用户返回标签时恢复播放。 用户不会在视频中丢失位置,视频的音轨不会干扰新前景选项卡中的音频,并且用户在此期间不会错过任何视频。这种体验是用户无感知的,并且对于用户体验是非常友好的。

因此规范的使用这个API可以减少对用户宽带的占用,减少服务器压力,节省用户内存,以及到达更好的播放效果。

3.罗列一些使用场景

1.网站有图片轮播效果,只有在用户观看轮播的时候,才会自动展示下一张幻灯片。
2.显示信息仪表盘的应用程序不希望在页面不可见时轮询服务器进行更新。
3.页面想要检测是否正在渲染,以便可以准确的计算网页浏览量(埋点使用场景)。
4.当设备进入待机模式时,网站想要关闭设备声音(用户按下电源键关闭屏幕)。

4. 该API的属性和事件

HTML5中专门为document元素添加了相关属性和事件:

属性:

1、Document.hidden 只读属性 布尔值 简单的表示标签页显示或者隐藏
如果页面处于被认为是对用户隐藏状态时返回true,否则返回false。
2、Document.visibilityState 只读属性
是一个用来展示文档可见性状态的字符串,可能的值:
visible: 页面内容至少是部分可见。 在实际中,这意味着页面是非最小化窗口的前景选项卡。
hidden : 页面内容对用户不可见。 在实际中,这意味着文档可以是一个后台标签,或是最小化窗口的一部分,或是在操作系统锁屏激活的状态下。
prerender : 页面内容正在被预渲染且对用户是不可见的(被document.hidden当做隐藏). 文档可能初始状态为prerender,但绝不会从其它值转为该值。 注释:浏览器支持是可选的。
unloaded : 页面正在从内存中卸载。 注释:浏览器支持是可选的。
3、Document.onvisibilitychange
EventListener 提供在visibilitychange 事件被触发时要调用的代码。

5.应用实例场景:带有声音的视频

在此例中,当你切换到另一个标签时视频会暂停,当你返回到当前标签时视频会再次播放,代码如下:

 <main>
    <video id="videoElement" controls="" poster="thumbnail.jpg">
        <source src="https://s3-ap-northeast-1.amazonaws.com/daniemon/demos/The%2BVillage-Mobile.mp4" type="video/mp4" media="all and (max-width:680px)"> 
        <source src="https://s3-ap-northeast-1.amazonaws.com/daniemon/demos/The%2BVillage-Mobile.webm" type="video/webm" media="all and (max-width:680px)"> 
        <source src="https://s3-ap-northeast-1.amazonaws.com/daniemon/demos/The%2BVillage-SD.mp4" type="video/mp4">
        <source src="https://s3-ap-northeast-1.amazonaws.com/daniemon/demos/The%2BVillage-SD.webm" type="video/webm">
        <p>Sorry, there's a problem playing this video. Please try using a different browser.</p>
    </video>
  </main>
// 设置隐藏属性和改变可见属性的事件的名称
    var hidden, visibilityChange; 
    if (typeof document.hidden !== "undefined") { // Opera 12.10 and Firefox 18 and later support 
      hidden = "hidden";
      visibilityChange = "visibilitychange";
    } else if (typeof document.msHidden !== "undefined") {
      hidden = "msHidden";
      visibilityChange = "msvisibilitychange";
    } else if (typeof document.webkitHidden !== "undefined") {
      hidden = "webkitHidden";
      visibilityChange = "webkitvisibilitychange";
    }

    var videoElement = document.getElementById("videoElement");

    // 如果页面是隐藏状态,则暂停视频
    // 如果页面是展示状态,则播放视频
    function handleVisibilityChange() {
      if (document[hidden]) {
        videoElement.pause();
      } else {
        videoElement.play();
      }
    }

    // 如果浏览器不支持addEventListener 或 Page Visibility API 给出警告
    if (typeof document.addEventListener === "undefined" || typeof document[hidden] === "undefined") {
      console.log("This demo requires a browser, such as Google Chrome or Firefox, that supports the Page Visibility API.");
    } else {
      
    // 处理页面可见属性的改变
    document.addEventListener(visibilityChange, handleVisibilityChange, false);
      
    // 当视频暂停,设置title
    // This shows the paused
    videoElement.addEventListener("pause", function(){
      document.title = 'Paused';
    }, false);
      
    // 当视频播放,设置title
    videoElement.addEventListener("play", function(){
      document.title = 'Playing'; 
    }, false);
  }

6兼容性处理

为了支持老版本的浏览器,我们需要对document.hidden在做一些前缀处理:

function getHiddenProp(){
        var prefixes = ['webkit','moz','ms','o'];
        // 如果hidden 属性是原生支持的,我们就直接返回
        if ('hidden' in document) {
          return 'hidden';
        }
        
        // 其他的情况就循环现有的浏览器前缀,拼接我们所需要的属性 
        for (var i = 0; i < prefixes.length; i++){
          // 如果当前的拼接的前缀在 document对象中存在 返回即可
          if ((prefixes[i] + 'Hidden') in document) {
            return prefixes[i] + 'Hidden';
          }  
        }
    
        // 其他的情况 直接返回null
        return null;
    }

同样的,我们可以获取document.visibilityState属性:

function getVisibilityState() {
    var prefixes = ['webkit', 'moz', 'ms', 'o'];

    if ('visibilityState' in document) {
      return 'visibilityState';
    }

    for (var i = 0; i < prefixes.length; i++) {
        if ((prefixes[i] + 'VisibilityState') in document){
          return prefixes[i] + 'VisibilityState';
        }  
    }
    // 找不到返回 null
    return null;
}

visibilitychange监听事件

你可以在document对象上注册一个监听visibilitychange事件,根据document.hidden或者document.visibilityState属性做一些业务逻辑:

var visProp = getHiddenProp();
if (visProp) {
    // 有些浏览器也需要对这个事件加前缀以便识别。
    var evtname = visProp.replace(/[H|h]idden/, '') + 'visibilitychange';

    document.addEventListener(evtname, function () {
        document.title = document[getVisibilityState()]+"状态";
    },false);
}

上面的代码会在页面可见性发生变化时修改document.title的值

上一篇:判断标签页显示隐藏(visibilitychange事件)


下一篇:jquery----Ajax