使用 Page Visibility API
检查文档可见性可以让你了解访问者如何与你的页面互动,并能提供关于应用程序状态的线索。 页面可见性 API 允许你了解页面的可见性,并设置事件监听器,以便在页面可见性发生变化时执行某些操作。让我们来看看页面可见性意味着什么,你如何使用这个 API,以及一些常见的陷阱需要避免。
什么是页面可见性 API?
最初,页面可见性 API 是作为一个 独立规范 开发的,但它已被直接合并到 HTML 规范中的 页面可见性 部分。这个 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,以确保访问者在浏览时拥有自主权。你也不应该假定访问者希望在页面隐藏时自动暂停媒体。强烈建议公开用户偏好设置或允许访问者选择加入此类功能。
MDN 上曾经有一个关于 visibilitychange 页面的代码片段,它很好地说明了这个 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 时,是否有其他模式是开发人员应该避免的?随时 与我们联系,让我们知道你的想法,或者我是否遗漏了什么!