Page Visibility API 教程

阮一峰發表於2018-10-25

一、簡介

有時候,開發者需要知道,使用者正在離開頁面。常用的方法是監聽下面三個事件。

  • pagehide
  • beforeunload
  • unload

但是,這些事件在手機上可能不會觸發,頁面就直接關閉了。因為手機系統可以將一個程序直接轉入後臺,然後殺死。

  • 使用者點選了一條系統通知,切換到另一個 App。
  • 使用者進入任務切換視窗,切換到另一個 App。
  • 使用者點選了 Home 按鈕,切換回主螢幕。
  • 作業系統自動切換到另一個 App(比如,收到一個電話)。

上面這些情況,都會導致手機將瀏覽器程序切換到後臺,然後為了節省資源,可能就會殺死瀏覽器程序。

以前,頁面被系統切換,以及系統清除瀏覽器程序,是無法監聽到的。開發者想要指定,任何一種頁面解除安裝情況下都會執行的程式碼,也是無法做到的。為了解決這個問題,就誕生了 Page Visibility API。不管手機或桌面電腦,所有情況下,這個 API 都會監聽到頁面的可見性發生變化。

這個新的 API 的意義在於,透過監聽網頁的可見性,可以預判網頁的解除安裝,還可以用來節省資源,減緩電能的消耗。比如,一旦使用者不看網頁,下面這些網頁行為都是可以暫停的。

  • 對伺服器的輪詢
  • 網頁動畫
  • 正在播放的音訊或影片

二、document.visibilityState

這個 API 主要在document物件上,新增了一個document.visibilityState屬性。該屬性返回一個字串,表示頁面當前的可見性狀態,共有三個可能的值。

  • hidden:頁面徹底不可見。
  • visible:頁面至少一部分可見。
  • prerender:頁面即將或正在渲染,處於不可見狀態。

其中,hidden狀態和visible狀態是所有瀏覽器都必須支援的。prerender狀態只在支援"預渲染"的瀏覽器上才會出現,比如 Chrome 瀏覽器就有預渲染功能,可以在使用者不可見的狀態下,預先把頁面渲染出來,等到使用者要瀏覽的時候,直接展示渲染好的網頁。

只要頁面可見,哪怕只露出一個角,document.visibilityState屬性就返回visible。只有以下四種情況,才會返回hidden

  • 瀏覽器最小化。
  • 瀏覽器沒有最小化,但是當前頁面切換成了背景頁。
  • 瀏覽器將要解除安裝(unload)頁面。
  • 作業系統觸發鎖屏螢幕。

可以看到,上面四種場景涵蓋了頁面可能被解除安裝的所有情況。也就是說,頁面解除安裝之前,document.visibilityState屬性一定會變成hidden。事實上,這也是設計這個 API 的主要目的。

另外,早期版本的 API,這個屬性還有第四個值unloaded,表示頁面即將解除安裝,現在已經被廢棄了。

注意,document.visibilityState屬性只針對頂層視窗,內嵌的<iframe>頁面的document.visibilityState屬性由頂層視窗決定。使用 CSS 屬性隱藏<iframe>頁面(比如display: none;),並不會影響內嵌頁面的可見性。

三、document.hidden

由於歷史原因,這個 API 還定義了document.hidden屬性。該屬性只讀,返回一個布林值,表示當前頁面是否可見。

document.visibilityState屬性返回visible時,document.hidden屬性返回false;其他情況下,都返回true

該屬性只是出於歷史原因而保留的,只要有可能,都應該使用document.visibilityState屬性,而不是使用這個屬性。

四、visibilitychange 事件

只要document.visibilityState屬性發生變化,就會觸發visibilitychange事件。因此,可以透過監聽這個事件(透過document.addEventListener()方法或document.onvisibilitychange屬性),跟蹤頁面可見性的變化。


document.addEventListener('visibilitychange', function () {
  // 使用者離開了當前頁面
  if (document.visibilityState === 'hidden') {
    document.title = '頁面不可見';
  }

  // 使用者開啟或回到頁面
  if (document.visibilityState === 'visible') {
    document.title = '頁面可見';
  }
});

上面程式碼是 Page Visibility API 的最基本用法,可以監聽可見性變化。

下面是另一個例子,一旦頁面不可見,就暫停影片播放。


var vidElem = document.getElementById('video-demo');
document.addEventListener('visibilitychange', startStopVideo);

function startStopVideo() {
  if (document.visibilityState === 'hidden') {
    vidElem.pause();
  } else if (document.visibilityState === 'visible') {
    vidElem.play();
  }
}

五、頁面解除安裝

下面專門討論一下,如何正確監聽頁面解除安裝。

頁面解除安裝可以分成三種情況。

  • 頁面可見時,使用者關閉 Tab 頁或瀏覽器視窗。
  • 頁面可見時,使用者在當前視窗前往另一個頁面。
  • 頁面不可見時,使用者或系統關閉瀏覽器視窗。

這三種情況,都會觸發visibilitychange事件。前兩種情況,該事件在使用者離開頁面時觸發;最後一種情況,該事件在頁面從可見狀態變為不可見狀態時觸發。

由此可見,visibilitychange事件比pagehidebeforeunloadunload事件更可靠,所有情況下都會觸發(從visible變為hidden)。因此,可以只監聽這個事件,執行頁面解除安裝時需要執行的程式碼,不用監聽後面那三個事件。

甚至可以這樣說,unload事件在任何情況下都不必監聽,beforeunload事件只有一種適用場景,就是使用者修改了表單,沒有提交就離開當前頁面。另一方面,指定了這兩個事件的監聽函式,瀏覽器就不會快取當前頁面。

六、參考連結

  • Page Visibility Level 2, W3C
  • Page Visibility API, David Walsh
  • Using the pageVisbility API, Joe Marini
  • Using PC Hardware more efficiently in HTML5: New Web Performance APIs, Part 2, Jatinder Mann
  • Don't lose user and app state, use Page Visibility, Ilya Grigorik

(完)

相關文章