Page Visibility API
页面可见性 API 提供了可供监视的事件,以便了解文档何时变为可见或隐藏,以及用于查看页面当前可见状态的功能。
这对于节省资源和提高性能特别有用,因为它允许页面在文档不可见时避免执行不必要的任务。
概念与用法
当用户最小化窗口、切换到另一个标签页,或者文档被另一个窗口完全遮挡时,API 会发送一个 visibilitychange 事件,让监听器知道页面的状态已更改。您可以检测到该事件并执行一些操作或表现出不同的行为。例如,如果您的 Web 应用正在播放视频,它可以在用户将标签页移到后台时暂停视频,并在用户返回标签页时恢复播放。用户不会丢失视频的进度,视频的音轨不会干扰新前台标签页中的音频,并且用户在此期间不会错过任何视频内容。
<iframe> 的可见性状态与父文档相同。使用 CSS 属性(如 display: none;)隐藏 <iframe> 不会触发可见性事件,也不会改变框架内文档的状态。
用例
让我们考虑页面可见性 API 的几个用例。
- 一个网站有一个图片轮播,除非用户正在查看页面,否则不应前进到下一张幻灯片
- 一个显示信息仪表板的应用程序不希望在页面不可见时轮询服务器以获取更新
- 一个网站希望在设备进入待机模式时关闭声音(用户按下电源按钮关闭屏幕)
开发人员过去曾使用不完美的代理来检测这一点。例如,监视窗口的 blur 和 focus 事件有助于了解何时您的页面不是活动页面,但它不能告诉您您的页面实际上对用户是隐藏的。页面可见性 API 解决了这个问题。
为提高后台页面性能而制定的策略
与页面可见性 API 分开,用户代理通常有许多策略来减轻后台或隐藏标签页的性能影响。这些可能包括
- 大多数浏览器会停止向后台标签页或隐藏的
<iframe>发送requestAnimationFrame()回调,以提高性能和电池寿命。 setTimeout()等计时器在后台/不活动标签页中受到限制,以帮助提高性能。有关更多详细信息,请参阅 setTimeout() 的延迟原因。- 浏览器实现了基于预算的后台超时限制。这在现代浏览器中的运行方式类似,具体细节如下:
- 在 Firefox 中,后台标签页中的窗口各有自己的时间预算(以毫秒为单位),最大值为 +50 ms,最小值为 -150 ms。Chrome 非常相似,只是预算以秒为单位指定。
- 窗口将在 30 秒后受到限制,与窗口计时器指定的限制延迟规则相同(同样,请参阅 setTimeout() 的延迟原因)。在 Chrome 中,此值为 10 秒。
- 只有当预算为非负数时,才允许计时器任务。
- 一旦计时器的代码运行完毕,它所花费的执行时间将从其窗口的超时预算中扣除。
- 在 Firefox 和 Chrome 中,预算以每秒 10 ms 的速率再生。
有些进程不受此限制行为的影响。在这些情况下,您可以使用页面可见性 API 来减少隐藏选项卡在隐藏时的性能影响。
- 正在播放音频的选项卡被视为前台,不会受到限制。
- 正在运行使用实时网络连接(WebSockets 和 WebRTC)的代码的选项卡不会受到限制,以避免关闭这些连接导致超时并意外关闭。
- IndexedDB 进程也不会受到限制,以避免超时。
其他接口的扩展
实例属性
页面可见性 API 向 Document 接口添加了以下属性:
-
如果页面处于对用户隐藏的状态,则返回
true,否则返回false。 Document.visibilityState只读-
一个指示文档当前可见状态的字符串。可能的值为:
visible-
页面内容至少部分可见。实际上,这意味着页面是未最小化窗口的前台标签页。
-
页面的内容对用户不可见,原因可能是文档的标签页处于后台,或者是一个已最小化的窗口的一部分,或者是因为设备的屏幕已关闭。
事件
页面可见性 API 向 Document 接口添加了以下事件:
visibilitychange-
当标签页的内容变得可见或被隐藏时触发。
示例
页面隐藏时暂停音频
此示例会在页面隐藏时暂停正在播放的音频,并在页面再次可见时恢复播放。<audio> 元素控件允许用户在播放和暂停音频之间切换。布尔值 playingOnHide 用于防止在页面切换到 visible 状态但媒体在页面隐藏时未播放的情况下播放音频。
HTML
<audio
controls
src="https://mdn.github.io/webaudio-examples/audio-basics/outfoxing.mp3"></audio>
JavaScript
const audio = document.querySelector("audio");
let playingOnHide = false;
document.addEventListener("visibilitychange", () => {
if (document.hidden) {
playingOnHide = !audio.paused;
audio.pause();
} else if (playingOnHide) {
// Page became visible! Resume playing if audio was "playing on hide"
audio.play();
}
});
结果
规范
| 规范 |
|---|
| HTML # dom-document-visibilitystate |
浏览器兼容性
加载中…