Page Visibility API - 提供了监视以及查看页面当前可见性状态的功能
Page Visibility API(页面可见性 API)提供了您可以监视的事件,以了解文档何时变为可见或隐藏,以及查看页面当前可见性状态的功能。
当用户最小化窗口或切换到另一个选项卡时,API 会发送一个 visibilitychange
事件,让侦听器知道页面的状态已更改。你可以检测到该事件并执行一些动作或不同的行为。例如,如果您的 Web 应用程序正在播放视频,当用户把标签放到后台时,它可以暂停视频,而当用户回到标签时恢复播放。用户不会丢失他们视频的播放进度,视频的配乐不会干扰新的前台标签的音频,同时用户不会错过任何视频。使用 CSS 属性(例如 display: none;
)隐藏 <iframe>
不会触发可见性事件或更改框架内包含的文档的状态。
用例
让我们考虑一些页面可见性 API 的用例。
- 网站有一个图像轮播,如果用户不在查看页面,则不应前进到下一张幻灯片
- 当页面不可见时,显示信息仪表板的应用程序不应轮询服务器以获取更新
- 页面想要检测它何时被预渲染,以便它可以准确计算页面浏览量
- 网站希望在设备处于待机模式时关闭声音(用户按下电源按钮关闭屏幕)
开发人员历来使用不完美的方式来检测这一点。例如,监视窗口上的 blur
和 focus
事件可以帮助您了解您的页面何时不是活动页面,但它不会告诉您用户是否看不到页面了。页面可见性 API 解决了这个问题。
注意: 虽然
onblur
和onfocus
会告诉你用户是否切换窗口,它并不一定意味着它是隐藏的。只有当用户切换选项卡或最小化包含该选项卡的浏览器窗口时,页面才会隐藏。
有助于后台页面性能的政策
与页面可见性 API 不同,用户代理通常有许多策略来减轻背景或隐藏选项卡的性能影响。这些可能包括:
- 大多数浏览器停止向后台选项卡或隐藏的
<iframe>
发送requestAnimationFrame()
回调,以提高性能和电池寿命。 setTimeout()
等计时器在后台 / 非活动选项卡中受到限制,以帮助提高性能。有关更多详细信息,请参阅 延迟时间超过指定的原因。- 基于预算的后台超时限制现在可在现代浏览器(Firefox 58+、Chrome 57+)中使用,对后台计时器 CPU 使用设置了额外限制。这在现代浏览器中以类似的方式运行,详细信息如下:
- 在 Firefox 中,后台选项卡中的每个窗口都有自己的时间预算(以毫秒为单位)—— 最大值和最小值分别为 +50 毫秒和 -150 毫秒。Chrome 非常相似,只是预算以秒为单位指定。
- Windows 会在 30 秒后进行节流,其节流延迟规则与为窗口计时器指定的相同(再次参见 延迟时间超过指定的原因)。在 Chrome 中,此值为 10 秒。
- 仅当预算为非负时才允许计时器任务。
- 一旦计时器的代码运行完毕,就会从其窗口的超时预算中减去执行所需的时间。
- 在 Firefox 和 Chrome 中,预算以每秒 10 毫秒的速度重新生成。
某些进程不受此限制行为的影响。在这些情况下,您可以使用页面可见性 API 来减少选项卡隐藏时的性能影响。
- 正在播放音频的选项卡被视为前台,不会受到限制。
- 运行使用实时网络连接(WebSockets and WebRTC)的代码的选项卡不受限制,以避免超时和意外关闭。
- IndexedDB 进程也不受限制以避免超时。
实例
查看在线实例(带声音的视频)。
该实例使用以下代码创建,当您切换到另一个选项卡时暂停视频并在您返回其选项卡时再次播放:
// 设置隐藏属性的名称和可见性的更改事件
var hidden, visibilityChange;
if (typeof document.hidden !== "undefined") { // Opera 12.10 和 Firefox 18 及更高版本支持
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");
// 如果页面被隐藏,暂停视频;
// 如果页面显示,播放视频
if (document[hidden]) {
videoElement.pause();
} else {
videoElement.play();
}
}
// 如果浏览器不支持 addEventListener 或页面可见性 API,则发出警告
if (typeof document.addEventListener === "undefined" || hidden === undefined) {
console.log("此演示需要一个支持页面可见性 API 的浏览器,例如 Google Chrome 或 Firefox。");
} else {
// 处理页面可见性变化
document.addEventListener(visibilityChange, handleVisibilityChange, false);
// 当视频暂停时,设置标题。
// 这里显示暂停
videoElement.addEventListener("pause", function(){
document.title = '暂停';
}, false);
// 播放视频时,设置标题。
videoElement.addEventListener("play", function(){
document.title = 'Playing';
}, false);
}
添加到文档接口的属性
页面可见性 API 将以下属性添加到 Document
接口:
Document.hidden
只读
如果页面处于对用户隐藏的状态,则返回 true
,否则返回 false
。
Document.visibilityState
只读
一个 DOMString
指示文档的当前可见性状态。可能的值为:
visible
页面内容可以至少部分可见。实际上,这意味着页面是非最小化窗口的前台选项卡。
hidden
页面的内容对用户不可见,原因可能是文档的选项卡在后台或窗口最小化了,或者因为设备的屏幕关闭了。
prerender
页面的内容正在预呈现,用户看不到。一个文档可能以 prerender
状态开始,但永远不会从任何其他状态切换到这个状态,因为一个文档只能预渲染一次。
注意: 并非所有浏览器都支持预渲染。
unloaded
该页面正在从内存中卸载。
注意: 并非所有浏览器都支持
unloaded
值。
Document.onvisibilitychange
一个 EventListener
,提供在触发 visibilitychange
事件时要调用的代码。
// 在其他地方定义了 startSimulation 和 pauseSimulation
function handleVisibilityChange() {
if (document.visibilityState === "hidden") {
pauseSimulation();
} else {
startSimulation();
}
}
document.addEventListener("visibilitychange", handleVisibilityChange, false);
规范
规范 |
---|
Page Visibility |
浏览器兼容性
Document.visibilityState
桌面浏览器兼容性
特性 | Chrome | Edge | Firefox | Internet Explorer | Opera | Safari |
---|---|---|---|---|---|---|
基础支持 | 33 13 webkit | 12 | 18 10 — 52 moz | 10 | 12.11 | 72 |
prerender value | 支持 | ≤79 | 49 | 未知 | 未知 | 未知 |
移动浏览器兼容性
特性 | Android | Chrome for Android | Edge mobile | Firefox for Android | IE mobile | Opera Android | iOS Safari |
---|---|---|---|---|---|---|---|
基础支持 | 4.4.3 | 33 支持 webkit | 未知 | 18 10 — 52 moz | 未知 | 12.11 | 72 |
prerender value | 支持 | 支持 | 未知 | 49 | 未知 | 未知 | 未知 |
1. 当浏览器窗口最小化时,或者 hidden
设置为 true
时,都不会触发 visibilitychange
事件。
2. 当 document.visibilityState
转换为 hidden
时不会触发 visibilitychange
事件,因此还需要加上代码来检查 pagehide
事件(在所有当前浏览器中都会触发这种情况)。请参阅 WebKit bugs 116769、151234、151610 和 194897。