
使用页面可见性 API
检查文档可见性可以让你洞悉访问者如何与你的页面互动,并提供关于你的应用程序状态的提示。 页面可见性 API 允许你了解页面的可见性,并设置事件监听器,以便在页面可见性发生变化时执行某些操作。让我们来看看页面可见性意味着什么,如何使用这个 API,以及一些常见的陷阱。
什么是页面可见性 API?
如何检查页面可见性更改
你可以使用 document.visibilityState
检查文档的可见性,它将返回 visible
或 hidden
。或者,你可以检查 document.hidden
布尔属性的值
console.log(document.visibilityState); // "visible"
console.log(document.hidden); // false
在实践中,使用 visibilitychange
事件很方便,这样你就可以在页面的可见性状态发生变化时触发逻辑,而不是手动检查可见性
document.addEventListener("visibilitychange", (event) => {
// doSomething();
});
当状态发生变化时,你可以检查页面可见性,然后根据结果执行某些操作
document.addEventListener("visibilitychange", () => {
if (document.hidden) {
// do something if the page visibility changes to hidden
} else {
// do something if the page visibility changes to visible
}
});
值得注意的是,没有 document.visible
,所以如果你只对该状态感兴趣,你可以使用 document.visibilityState === "visible"
(或 !document.hidden
)
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "visible") {
// The page is visible again!
}
});
页面可见性为什么有用
页面可见性在许多情况下都有用,但最有可能的应用场景是在分析、管理计算资源的使用以及添加可以改善各种设备的用户体验 (UX) 的功能。让我们更详细地了解一下这些方面。
分析中的会话生命周期
在分析领域,记录页面从 visible
变为 hidden
的时刻很常见。页面变为 hidden
状态可能是页面可以观察到的最后一个事件,因此开发人员通常将它视为用户会话的结束。但是,这在很大程度上取决于你如何定义 "一个会话"。你可以根据一段固定的空闲时间来定义一个会话,而不是仅仅依赖于 "第一次页面隐藏"。因此,这个用例会根据你的需求而有所不同
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "hidden") {
navigator.sendBeacon("/log", analyticsData);
}
};
有效管理资源
确定页面可见性的能力为我们提供了一个机会,当访问者不再查看页面时,停止执行某些操作。考虑到这使我们能够以一种有意识的方式管理客户端(甚至服务器)资源,这种能力非常强大。
在管理资源方面,浏览器在这方面发挥着重要作用,例如 标签卸载 和优化 后台标签的行为。我们仍然可以通过查看如何构建 Web 应用程序来将解决方案向左移动。这使我们能够在开发周期的早期阶段包含效率检查,或者在浏览器无法自动处理的领域进行有意识的性能改进。
如果使用较低比特率的视频,或者如果与服务器进行定期实时通信(WebSockets 和 WebRTC),则可以限制或暂停网络活动。IndexedDB 进程可以优化,因为浏览器不会限制这些连接以避免超时。你可以在 页面可见性 API 页面 上的 "实施的策略以帮助提高后台页面性能" 部分找到有关浏览器如何处理这些问题的更多信息。
改善用户体验
当页面从 hidden
变为 visible
时,我们可以假设访问者已返回我们的页面,因此我们可以重新启动任何在页面隐藏时可能暂停的操作。但是,有一些逻辑陷阱,尤其是与媒体有关的陷阱,你可能会陷入其中。因此,在这些情况下恢复操作时,你需要小心。我们将在下一节中详细讨论这些示例。
使用页面可见性检查时应避免的模式
赋予人们控制何时开始、恢复和跳过媒体的能力对于页面可用性至关重要,因此应谨慎使用这个 API,以确保你的访问者在浏览时拥有自主权。你也不能假设访问者希望在页面隐藏时自动暂停媒体。公开用户偏好或允许访问者选择此类功能非常值得推荐。
在 visibilitychange
页面上的 MDN 上曾经有一个代码片段,这是一个很好的示例,说明了这个 API 如何导致可用性问题。你能发现下面 JavaScript 代码中的问题吗?
<audio
controls
src="https://mdn.github.io/webaudio-examples/audio-basics/outfoxing.mp3"></audio>
const audio = document.querySelector("audio");
document.addEventListener("visibilitychange", () => {
if (document.hidden) {
audio.pause();
} else {
audio.play();
}
});
在 HTML 中,有一个带有可见控制的 <audio>
元素,以便用户可以启动和停止媒体。在 JavaScript 中,我们正在查看 visibilitychange
事件,并执行以下操作
- 如果页面隐藏,则暂停音频。
- 如果页面显示,则播放音频。
乍一看这似乎很合理,但我们会遇到一些问题,例如,当用户切换到另一个标签然后再切换回来,或者在最小化浏览器窗口后恢复浏览器窗口时,音频会自动播放。关键是页面可见性可能会在 hidden
和 visible
之间切换,而用户从未与 <audio>
元素互动。
如果 <audio>
元素位于页面折叠下方并且在页面加载时不可见,这种情况会更加令人惊讶和令人分心。你可能会无意中促使用户搜索正在播放音频的标签,并引导他们到一个无法清楚识别正在播放内容的地方,这可能非常令人沮丧。
为了避免可用性问题,最好先检查用户是否与媒体互动,然后再恢复播放。一种方法是在页面隐藏时存储音频播放器的状态。当页面可见性再次变为 visible
时,我们只在页面隐藏时正在播放的情况下恢复媒体播放。
让我们修改事件监听器,以便在页面隐藏时存储音频状态。一个布尔值 playingOnHide
变量足以实现这个目的;我们可以在文档可见性变为 hidden
时设置它
// Initialize as false; we're not auto playing audio when the page loads
let playingOnHide = false;
document.addEventListener("visibilitychange", () => {
if (document.hidden) {
// Page changed to "hidden" state, store if audio playing
playingOnHide = !audio.paused;
// Pause audio if page is hidden
audio.pause();
}
});
与 document.hidden
一样,没有 audio.playing
状态,因此我们必须检查 !audio.paused
(未暂停)而不是。这意味着 playingOnHide
布尔值可以设置为页面隐藏时 !audio.paused
的值,我们已经完成了大部分工作。页面的唯一其他状态是 visible
,因此在 else
语句中,我们处理播放逻辑
let playingOnHide = false;
document.addEventListener("visibilitychange", () => {
if (document.hidden) {
playingOnHide = !audio.paused;
audio.pause();
} else {
// Page became visible! Resume playing if audio was "playing on hide"
if (playingOnHide) {
audio.play();
}
}
});
完成后的代码如下所示,带有一个用于在页面隐藏时检查音频状态的小门
<audio
controls
src="https://mdn.github.io/webaudio-examples/audio-basics/outfoxing.mp3"></audio>
const audio = document.querySelector("audio");
let playingOnHide = false;
document.addEventListener("visibilitychange", () => {
if (document.hidden) {
playingOnHide = !audio.paused;
audio.pause();
} else {
if (playingOnHide) {
audio.play();
}
}
});
总结
我希望你喜欢这篇关于页面可见性的文章,以及我认为它为什么为资源管理、分析、UX 和其他用例带来了有趣的机会。我期待听到你对使用这个 API 的不同方法的见解,以及开发人员可以利用哪些其他效率提升,而这些效率提升是浏览器无法很好地处理的。开发人员在使用这个 API 时还有哪些其他模式应该避免?随时 与我们联系,让我们知道你的想法,或者如果我遗漏了什么!