Edited at

[iOS/Android]ブラウザでページが非表示になったことを検知する方法

More than 3 years have passed since last update.


やりたいこと

webページが非表示になったことを検知して、処理を行いたい


検証


検証環境


  • iOS : iPhone6 (9.3.1)

  • Android : Xperia501SO (5.1.1)

  • Proxy : BurpSuite (1.6.32)


検証内容(その1)

beforeunload / pagehide / unloadイベントを仕込んでやってみた


検証結果(その1)

[iOS]

動作
発生イベント

ページリロード
pagehide
unload

リンクタップ
pagehide
unload

リンクタップ(target blunk)
-

ブラウザバック
pagehide
unload

電源ボタンタップ
-

ホームボタン
-

ホームボタン(ダブルタップ)
-

[Android]

動作
発生イベント

ページリロード
beforeunload
pagehide
unload

リンクタップ
beforeunload
pagehide
unload

リンクタップ(target blunk)
-

ブラウザバック
beforeunload
pagehide
unload

電源ボタンタップ
-

ホームボタン
-

アプリ履歴ボタンタップ
-


考察(その1)

結果からわかる通り、デバイスに備わっているボタンをタップした時のイベントが拾えていない

iOSは、beforeunloadが呼ばれないみたい


検証内容(その2)

次に、Page Visibility APIを仕込んでやってみた


検証結果(その2)

[iOS]

動作
発生イベント

ページリロード
-

リンクタップ
-

リンクタップ(target blunk)
-

ブラウザバック
-

電源ボタンタップ
visibilityChange

ホームボタン
visibilityChange

ホームボタン(ダブルタップ)
-

ホームボタン(ダブルタップ)→電源ボタン
visibilityChange

[Android]

動作
発生イベント

ページリロード
-

リンクタップ
-

リンクタップ(target blunk)
visibilityChange

ブラウザバック
-

電源ボタンタップ
visibilityChange

ホームボタン
visibilityChange

アプリ履歴ボタンタップ
visibilityChange


考察(その2)

各種デバイスのボタンイベント後に、visibilityChangeイベントが呼ばれページが非表示になったことは検知できた

ただし、どの動作が行われたかは、検知できないみたい

iOSの場合、ホームボタンのダブルタップでは、visibilityChangeイベントが呼ばれず、その後、電源ボタンを押してみたところ、visibilityChangeイベントが呼ばれた


結果まとめ

以上の検証結果からbeforeunload / pagehide / unloadイベントとPage Visibility APIを組み合わせるとページが非表示になる動作を大体捉えられることがわかった

ページ遷移系のイベントは、とりあえず、pagehideunloadを読んでおけば大丈夫

iOS8とかAndroid 4/6系でどう動くかは、確認していないので、そのうち確認する予定


サンプルコード


sample.js

// デバイスのボタンイベントを検知するためのリスナー

function setVisibilityEvent(){
var hidden, visibilityChange;
if (typeof document.hidden !== "undefined") {
hidden = "hidden";
visibilityChange = "visibilitychange";
} else if (typeof document.mozHidden !== "undefined") {
hidden = "mozHidden";
visibilityChange = "mozvisibilitychange";
} else if (typeof document.msHidden !== "undefined") {
hidden = "msHidden";
visibilityChange = "msvisibilitychange";
} else if (typeof document.webkitHidden !== "undefined") {
hidden = "webkitHidden";
visibilityChange = "webkitvisibilitychange";
}
document.addEventListener(visibilityChange, function(){
// ページ非表示と表示時に呼ばれるため、表示から非表示になった時だけ呼ばれるように
// document.hidden条件を追加
if(document.hidden){
request("visibilitychange");
}
}, false);
}

// ページ遷移イベントを検知するためのリスナー
function setPageTransitionListener(){
window.addEventListener("pagehide", function(){
request("pagehide");
}, false);
}

// 非表示時の処理
// 以下の場合、サーバに通知する処理
function request(event){
var id = 'test_'+window.btoa(event);
var img = document.createElement('img');
var endPoint = 'http://...';
img.src = endPoint + query;
img.id = id;
body.appendChild(img);
img.parentNode.removeChild(img);
}

function main(){
setVisibilityEvent();
setPageTransitionListener();
}

main();