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.

使用 Page Visibility API

閱讀時間 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,以確保訪問者在瀏覽時擁有自主權。你也不應該假定訪問者希望在頁面隱藏時自動暫停媒體。強烈建議公開使用者偏好設定或允許訪問者選擇加入此類功能。

MDN 上曾經有一個關於 visibilitychange 頁面的程式碼片段,它很好地說明了這個 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 時,是否有其他模式是開發人員應該避免的?隨時 與我們聯絡,讓我們知道你的想法,或者我是否遺漏了什麼!