Using the Page Visibility API title. A vibrant gradient background with artwork of a laptop in the top-right corner and a JavaScript logo in the bottom-left corner.

使用页面可见性 API

作者头像Brian Smith阅读时长 6 分钟

检查文档可见性可以让你洞悉访问者如何与你的页面互动,并提供关于你的应用程序状态的提示。 页面可见性 API 允许你了解页面的可见性,并设置事件监听器,以便在页面可见性发生变化时执行某些操作。让我们来看看页面可见性意味着什么,如何使用这个 API,以及一些常见的陷阱。

什么是页面可见性 API?

最初,页面可见性 API 作为 独立规范 开发,但它已被直接整合到 HTML 规范中,位于 页面可见性 下。这个 API 提供了一种方法来确定文档的可见性状态,因此你可以检查一个网页是 "可见" 还是 "隐藏"。这听起来很简单,但页面可以通过多种方式被 "隐藏"。

如果包含页面的浏览器窗口被最小化,或者被另一个应用程序完全遮盖,或者用户切换到另一个标签,页面就会被 "隐藏"。如果操作系统屏幕锁定被激活,页面可见性也会变为 "隐藏",因此移动设备的行为也被考虑在内。相反,即使页面在屏幕上部分可见,它仍然保持 "可见"。

如何检查页面可见性更改

你可以使用 document.visibilityState 检查文档的可见性,它将返回 visiblehidden。或者,你可以检查 document.hidden 布尔属性的值

js
console.log(document.visibilityState); // "visible"
console.log(document.hidden); // false

在实践中,使用 visibilitychange 事件很方便,这样你就可以在页面的可见性状态发生变化时触发逻辑,而不是手动检查可见性

js
document.addEventListener("visibilitychange", (event) => {
  // doSomething();
});

当状态发生变化时,你可以检查页面可见性,然后根据结果执行某些操作

js
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

js
document.addEventListener("visibilitychange", () => {
  if (document.visibilityState === "visible") {
    // The page is visible again!
  }
});

页面可见性为什么有用

页面可见性在许多情况下都有用,但最有可能的应用场景是在分析、管理计算资源的使用以及添加可以改善各种设备的用户体验 (UX) 的功能。让我们更详细地了解一下这些方面。

分析中的会话生命周期

在分析领域,记录页面从 visible 变为 hidden 的时刻很常见。页面变为 hidden 状态可能是页面可以观察到的最后一个事件,因此开发人员通常将它视为用户会话的结束。但是,这在很大程度上取决于你如何定义 "一个会话"。你可以根据一段固定的空闲时间来定义一个会话,而不是仅仅依赖于 "第一次页面隐藏"。因此,这个用例会根据你的需求而有所不同

js
document.addEventListener("visibilitychange", () => {
  if (document.visibilityState === "hidden") {
    navigator.sendBeacon("/log", analyticsData);
  }
};

有效管理资源

确定页面可见性的能力为我们提供了一个机会,当访问者不再查看页面时,停止执行某些操作。考虑到这使我们能够以一种有意识的方式管理客户端(甚至服务器)资源,这种能力非常强大。

在管理资源方面,浏览器在这方面发挥着重要作用,例如 标签卸载 和优化 后台标签的行为。我们仍然可以通过查看如何构建 Web 应用程序来将解决方案向左移动。这使我们能够在开发周期的早期阶段包含效率检查,或者在浏览器无法自动处理的领域进行有意识的性能改进。

如果使用较低比特率的视频,或者如果与服务器进行定期实时通信(WebSockets 和 WebRTC),则可以限制或暂停网络活动。IndexedDB 进程可以优化,因为浏览器不会限制这些连接以避免超时。你可以在 页面可见性 API 页面 上的 "实施的策略以帮助提高后台页面性能" 部分找到有关浏览器如何处理这些问题的更多信息。

改善用户体验

当页面从 hidden 变为 visible 时,我们可以假设访问者已返回我们的页面,因此我们可以重新启动任何在页面隐藏时可能暂停的操作。但是,有一些逻辑陷阱,尤其是与媒体有关的陷阱,你可能会陷入其中。因此,在这些情况下恢复操作时,你需要小心。我们将在下一节中详细讨论这些示例。

使用页面可见性检查时应避免的模式

赋予人们控制何时开始、恢复和跳过媒体的能力对于页面可用性至关重要,因此应谨慎使用这个 API,以确保你的访问者在浏览时拥有自主权。你也不能假设访问者希望在页面隐藏时自动暂停媒体。公开用户偏好或允许访问者选择此类功能非常值得推荐。

visibilitychange 页面上的 MDN 上曾经有一个代码片段,这是一个很好的示例,说明了这个 API 如何导致可用性问题。你能发现下面 JavaScript 代码中的问题吗?

html
<audio
  controls
  src="https://mdn.github.io/webaudio-examples/audio-basics/outfoxing.mp3"></audio>
js
const audio = document.querySelector("audio");

document.addEventListener("visibilitychange", () => {
  if (document.hidden) {
    audio.pause();
  } else {
    audio.play();
  }
});

在 HTML 中,有一个带有可见控制的 <audio> 元素,以便用户可以启动和停止媒体。在 JavaScript 中,我们正在查看 visibilitychange 事件,并执行以下操作

  • 如果页面隐藏,则暂停音频。
  • 如果页面显示,则播放音频。

乍一看这似乎很合理,但我们会遇到一些问题,例如,当用户切换到另一个标签然后再切换回来,或者在最小化浏览器窗口后恢复浏览器窗口时,音频会自动播放。关键是页面可见性可能会在 hiddenvisible 之间切换,而用户从未与 <audio> 元素互动。

如果 <audio> 元素位于页面折叠下方并且在页面加载时不可见,这种情况会更加令人惊讶和令人分心。你可能会无意中促使用户搜索正在播放音频的标签,并引导他们到一个无法清楚识别正在播放内容的地方,这可能非常令人沮丧。

为了避免可用性问题,最好先检查用户是否与媒体互动,然后再恢复播放。一种方法是在页面隐藏时存储音频播放器的状态。当页面可见性再次变为 visible 时,我们只在页面隐藏时正在播放的情况下恢复媒体播放。

让我们修改事件监听器,以便在页面隐藏时存储音频状态。一个布尔值 playingOnHide 变量足以实现这个目的;我们可以在文档可见性变为 hidden 时设置它

js
// 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 语句中,我们处理播放逻辑

js
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();
    }
  }
});

完成后的代码如下所示,带有一个用于在页面隐藏时检查音频状态的小门

html
<audio
  controls
  src="https://mdn.github.io/webaudio-examples/audio-basics/outfoxing.mp3"></audio>
js
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 时还有哪些其他模式应该避免?随时 与我们联系,让我们知道你的想法,或者如果我遗漏了什么!

关注 MDN 的最新消息

订阅 MDN 时事通讯,不错过任何关于最新 Web 开发趋势、技巧和最佳实践的更新。