妙趣横生的HTML5 Page Visibility API

妙趣横生的HTML5 Page Visibility API

最近浏览36kr 的网页的时候偶然发现一个有趣的情况:当浏览器Tab 处于非当前页的时候,36kr 的标题栏会自动换成可爱的大白字符形象。Jeff 在第一次看到的时候就知道这种效果是通过HTML5 的Page Visibility API 实现的(不是吹牛)。

直到现在我还没去查看36kr 的源代码看其是如何实现的(也不打算去一个个查看它繁杂的js 文件了),HTML5 的Page Visibility API 以前看过,看到36kr 实现了这个的时候一时兴起,遂去查看文档深入了解之。然后三两下在自己的博客上实现了类似的效果(你可以切换Tab 到背后看看):

深入解析Page Visibility API

自从腾讯面试二面后,深深意识到“不去刨根问底的工程师跟搬砖的没啥区别”;虽然我一直认为好的技术文不应该过多复制API 文档的内容,但在这里还是从自己的角度为没接触过这个Page Visibility API 的同志们解析下。

荒芜年代一统天下的IE6 早已成为过去时(也许你还记得以前用IE6的时候每打开一个页面就弹出一个新的窗口,层层叠叠),现代浏览器在多Tab (标签后窗口)的构建形式上基本达成了共识,通常而言我们都是打开新标签页,在当前浏览器窗口中,每次都只有一个标签页处于激活态(或者说高亮),其余均为隐藏态。HTML5 的那帮搞标准的也适时提出了这个Page Visibility API——以期更好地利用这个“时隐时现”的动作。

在这里我们先稍微离题下,可能你知道,Chrome 浏览器较一般浏览器能更好稳定运行的原因在于其引入了沙盒机制,每个新建的标签页甚至是每个扩展都是独立在一个沙盒运行的。但如此一来带来的是系统资源的占用(Chrome 因耗内存被诟病就是如此来源于此)。回到正文,浏览器中每个标签页无论是激活态还是隐藏态,运作机制模式基本没啥区别的,原来该计算的还是在计算,原来在放视频的还是在放视频,占内存的还是在占内存。

但引入了Page Visibility API ,开发者可以做一些事情,让我们设想下面的场景并提供相应的demo:

场景一:视频网站用户在看视频时候切换到另外一个标签页了,视频自动暂停(点击查看Demo);

场景二:一些耗性能的页面在标签页处于隐藏状态时候自动停止相关运算,节省资源(点击查看Demo,援引自alloyteam);

场景三:好玩的,比如类似36kr 或本站的效果(点击查看Demo)。

场景N:桌面提醒Notification 开启隐藏态免打扰模式、流式加载的新闻页面在处于隐藏态时偷偷加载最新内容……

基本上,有了这个API,制约你的就是你的idea,你的想象力了。

Page Visibility API使用方法

Page Visibility API 有两个相关的属性:document.visibilityStatedocument.hidden

document.visibilityState有如下四个值:

hidden:当浏览器最小化、切换tab(the page is on a background tab)、电脑锁屏时visibilityState值是hidden

visible:当浏览器顶级context(top level browsing context)的document至少显示在一个屏幕(screen)当中时,返回visible;当浏览器窗口没有最小化,但是浏览器被其他应用遮挡时,visibilityState值也是visible

prerender:文档加载离屏(is loaded off-screen)或者不可见时返回prerender,浏览器可选择性的支持这个属性(not all browsers will necessarily support it)

unloaded:当文档(document)将要被unload时返回unloaded,浏览器可选择性的支持这个属性

document.hidden 是个布尔值属性,标签页处于隐藏态则为false,反之激活态为true。

最核心的是通过绑定visibilitychange 事件来达到需求:

document.addEventListener('visibilitychange', function(event) {
  if (!document.hidden) {
    // The page is visible.
  } else {
  // The page is hidden.
  }
});

因为兼容性的原因需要为相关函数添加不同的浏览器前缀:

// Get Browser-Specifc Prefixfunction getBrowserPrefix() {
 
  // Check for the unprefixed property.  if ('hidden' in document) {
    return null;
  }
  // All the possible prefixes.  var browserPrefixes = ['moz', 'ms', 'o', 'webkit'];
 
  for (var i = 0; i < browserPrefixes.length; i++) {
    var prefix = browserPrefixes[i] + 'Hidden';
    if (prefix in document) {
      return browserPrefixes[i];
    }  }
  // The API is not supported in browser.  return null;
}
// Get Browser Specific Hidden Propertyfunction hiddenProperty(prefix) {
  if (prefix) {
    return prefix + 'Hidden';
  } else {
    return 'hidden';
  }}
// Get Browser Specific Visibility Statefunction visibilityState(prefix) {
  if (prefix) {
    return prefix + 'VisibilityState';
  } else {
    return 'visibilityState';
  }}
// Get Browser Specific Eventfunction visibilityEvent(prefix) {
  if (prefix) {
    return prefix + 'visibilitychange';
  } else {
    return 'visibilitychange';
  }}

更多使用方法请查看上面的Demo 中的源代码。

=========

3.27 更新:写完后才发现介绍该API的中文文章已经是满大街了,唉又浪费我宝贵的时间了,下次提笔想写之前还是看看有没有人已经写过类似的文章吧。

部分参考来源:

https://developer.mozilla.org/en-US/docs/Web/Guide/User_experience/Using_the_Page_Visibility_API

http://www.alloyteam.com/2012/11/page-visibility-api/

http://code.tutsplus.com/articles/html5-page-visibility-api--cms-22021

评分:
当前平均分 4.86 (88%) - 7 个投票
云左主机 广告
4 条 评论
  1. 前阵子还在想36Kr到底怎么做到的,原来是这样!

    2年前 回复
  2. 很有用。多谢

    2年前 回复
  3. Jeff 你是 AlloyTeam 的??

    2年前 回复
    • 当然不是啊,你没看到文末有参考来源么。。。

      2年前 回复
发表评论