問題は、イベント「visibilitychange」の動作にあります。
トリガーされます:- ブラウザ ウィンドウ内で別のタブに切り替えたとき。
- ブラウザ ウィンドウの最小化/復元ボタンをクリックするとき。
(これで結構です)
トリガーされません:- ALT+TAB を使用して別のウィンドウ/プログラムに切り替えるとき。
- タスクバーをクリックして別のウィンドウ/プログラムに切り替えるとき。
(最小化時と同様にウィンドウの可視性が変わる可能性があるため、これをトリガーする必要があります)
W3 ページ可視性 API ドキュメント:http://www.w3.org/TR/page-visibility/
仕様書には、ALT+ /プログラムの切り替えに関する「ページの可視性」の定義がありません。OS とブラウザの間で何か関係があるのではないかと思います。TAB
テスト済み
- ブラウザ: Chrome 40.0.2214.115 m / Firefox 36.0.1 / Internet Explorer 11.0.9600.17107
- OS: Windows 8.1
この動作を修正する回避策はありますか? 実装は非常にシンプルで、jQuery を使用して「visibilitychange」イベントをリッスンし、そのコールバックで「document.visibilityState」の値を確認しますが、問題はイベントが予期したとおりに発生しないことです。
$(document).on('visibilitychange', function() {
if(document.visibilityState == 'hidden') {
// page is hidden
} else {
// page is visible
}
});
これは jQuery なしでも実行できますが、ALT+TABとタスクバーの非表示/表示を切り替える期待される動作がまだありません。
if(document.addEventListener){
document.addEventListener("visibilitychange", function() {
// check for page visibility
});
}
ifvisible.jsモジュールも試してみました(https://github.com/serkanyersen/ifvisible.js) ですが、動作は同じです。
ifvisible.on('blur', function() {
// page is hidden
});
ifvisible.on('focus', function() {
// page is visible
});
Windows 上の Chrome で動作させることができなければ、他のブラウザについてはまだ気にしていないので、他のブラウザではテストしていません。
何か助言や提案はありますか?
アップデート
イベント名に異なるベンダー プレフィックス (visibilitychange、webkitvisibilitychange、mozvisibilitychange、msvisibilitychange) を使用しようとしましたが、タスクバーまたはALT+で別のプログラムに切り替えてもTAB、または画面全体をカバーする Windows キーを使用して Windows のスタート メニューを開いた場合でも、イベントはトリガーされません。
Chrome、Firefox、Internet Explorer でもまったく同じ問題を再現できます。
アップデート#2
こちらはこの問題に関して私が書いたまとめ記事と、発生した問題を解決するための純粋な JavaScript による回避策。
アップデート #3
ソースのブログ投稿のコピーを含めるように編集しました。(承認された回答を参照)
ベストアンサー1
こちらはこの問題に関して私が書いたまとめ記事と、発生した問題を解決するための純粋な JavaScript による回避策。
ソースのブログ投稿のコピーを含めるように編集しました:
私たちが開発するあらゆる種類の JavaScript アプリケーションには、現在のユーザーの可視状態に応じて反応する機能やアプリケーションの変更が含まれる場合があります。たとえば、ユーザーが ALT+TAB で別のウィンドウに移動したときに再生中のビデオを一時停止したり、ユーザーがアプリケーションとどのようにやり取りしているか、ユーザーが別のタブに切り替える頻度、戻るのにどのくらいの時間がかかるかなどの統計を追跡したり、この種の API から恩恵を受けることができる多くのパフォーマンス改善を行ったりすることができます。
Page Visibility API は、document.hidden (ブール値) と document.visibilityState (「hidden」、「visible」、「prerender」、「unloaded」のいずれかの文字列) という 2 つのトップレベル属性を提供します。ただし、リッスンできるイベントがなければ十分ではありません。そのため、API は便利な visibilitychange イベントも提供します。
可視性の変更に対してどのようにアクションを実行するかについての基本的な例を次に示します。
function handleVisibilityChange() { if(document.hidden) { // the page is hidden } else { // the page is visible } } document.addEventListener("visibilitychange", handleVisibilityChange, false);
document.visibilityState の値を確認することもできます。
ベンダーの問題への対処 George Berkeley by John Smibert
一部のブラウザの実装では、属性またはイベント名にベンダー プレフィックスを付ける必要があるため、msvisibilitychange イベントをリッスンするか、document.webkitHidden または document.mozHidden 属性をチェックする必要があります。これを行うには、ベンダー プレフィックスの属性が設定されているかどうかを確認する必要があります。現在のブラウザで使用されている属性がわかれば (プレフィックスが必要な場合のみ)、イベントと属性に適切な名前を付けることができます。
これらのプレフィックスを処理する方法の例を次に示します。
var browserPrefixes = ['moz', 'ms', 'o', 'webkit']; // get the correct attribute name function getHiddenPropertyName(prefix) { return (prefix ? prefix + 'Hidden' : 'hidden'); } // get the correct event name function getVisibilityEvent(prefix) { return (prefix ? prefix : '') + 'visibilitychange'; } // get current browser vendor prefix function getBrowserPrefix() { for (var i = 0; i < browserPrefixes.length; i++) { if(getHiddenPropertyName(browserPrefixes[i]) in document) { // return vendor prefix return browserPrefixes[i]; } } // no vendor prefix needed return null; } // bind and handle events var browserPrefix = getBrowserPrefix(); function handleVisibilityChange() { if(document[getHiddenPropertyName(browserPrefix )]) { // the page is hidden console.log('hidden'); } else { // the page is visible console.log('visible'); } } document.addEventListener(getVisibilityEvent(browserPrefix), handleVisibilityChange, false);
その他の問題 「ページの可視性」の定義には難しい問題があります。ウィンドウのフォーカスが別のウィンドウに失われても、画面上の実際の可視性は失われない場合に、アプリケーションが可視であるかどうかを判断するにはどうすればよいでしょうか。ALT + TAB、WIN/MAC キー (スタート メニュー / ダッシュ)、タスク バー / ドックの操作、WIN + L (画面のロック)、ウィンドウの最小化、ウィンドウを閉じる、タブの切り替えなど、さまざまな種類の可視性の喪失についてはどうでしょうか。モバイル デバイスでの動作はどうでしょうか。
可視性を失ったり得たりする方法は数多くあり、ブラウザと OS の間で多くのやり取りが行われる可能性があるため、W3C 仕様には適切で完全な「可視ページ」の定義はないと思います。以下は document.hidden 属性の定義です。
HIDDEN 属性 取得時に、トップレベルのブラウジング コンテキスト (ブラウザーのビューポートのルート ウィンドウ) [HTML5] に含まれるドキュメントがまったく表示されない場合は、hidden 属性は true を返す必要があります。トップレベルのブラウジング コンテキストに含まれるドキュメントが少なくとも 1 つの画面で少なくとも部分的に表示されている場合、この属性は false を返す必要があります。
ドキュメントの defaultView が null の場合、取得時に、hidden 属性は true を返す必要があります。
通常は全画面表示ですが、ページのビューを表示するアクセシビリティ ツールに対応するために、該当する場合、ユーザー エージェントが最小化されていないが他のアプリケーションによって完全に隠されている場合、この属性は false を返すことがあります。
イベントが実際に発生するタイミングに関して、いくつかの矛盾点が見つかりました。たとえば、(Chrome 41.0.2272.101 m、Windows 8.1 の場合)、ALT+TAB で別のウィンドウ/プログラムに移動したときや、ALT+TAB をもう一度押して戻ったときにはイベントは発生しませんが、CTRL+TAB を押してから CTRL+SHIFT+TAB を押してブラウザのタブを切り替えるとイベントが発生します。最小化ボタンをクリックしたときにもイベントが発生しますが、ウィンドウが最大化されておらず、ブラウザ ウィンドウの背後にあるエディター ウィンドウをクリックした場合はイベントは発生しません。そのため、この API の動作とそのさまざまな実装はまだ不明瞭です。
この問題を回避するには、より適切に実装されたフォーカスおよびぼかしイベントを利用して補正し、内部フラグを使用して複数の実行を防ぐという「ページの可視性」の問題全体に対するカスタム アプローチを作成します。私が思いついたのは次の方法です。
var browserPrefixes = ['moz', 'ms', 'o', 'webkit'], isVisible = true; // internal flag, defaults to true // get the correct attribute name function getHiddenPropertyName(prefix) { return (prefix ? prefix + 'Hidden' : 'hidden'); } // get the correct event name function getVisibilityEvent(prefix) { return (prefix ? prefix : '') + 'visibilitychange'; } // get current browser vendor prefix function getBrowserPrefix() { for (var i = 0; i < browserPrefixes.length; i++) { if(getHiddenPropertyName(browserPrefixes[i]) in document) { // return vendor prefix return browserPrefixes[i]; } } // no vendor prefix needed return null; } // bind and handle events var browserPrefix = getBrowserPrefix(), hiddenPropertyName = getHiddenPropertyName(browserPrefix), visibilityEventName = getVisibilityEvent(browserPrefix); function onVisible() { // prevent double execution if(isVisible) { return; } // change flag value isVisible = true; console.log('visible'); } function onHidden() { // prevent double execution if(!isVisible) { return; } // change flag value isVisible = false; console.log('hidden'); } function handleVisibilityChange(forcedFlag) { // forcedFlag is a boolean when this event handler is triggered by a // focus or blur eventotherwise it's an Event object if(typeof forcedFlag === "boolean") { if(forcedFlag) { return onVisible(); } return onHidden(); } if(document[hiddenPropertyName]) { return onHidden(); } return onVisible(); } document.addEventListener(visibilityEventName, handleVisibilityChange, false); // extra event listeners for better behaviour document.addEventListener('focus', function() { handleVisibilityChange(true); }, false); document.addEventListener('blur', function() { handleVisibilityChange(false); }, false); window.addEventListener('focus', function() { handleVisibilityChange(true); }, false); window.addEventListener('blur', function() { handleVisibilityChange(false); }, false);
この回避策に関するフィードバックをお待ちしています。このテーマに関するアイデアの参考になる他の優れた情報源:
Page Visibility API の使用 HTML5 で PC ハードウェアをより効率的に使用する: 新しい Web パフォーマンス API、パート 2 Page Visibility API の概要 結論 Web のテクノロジーは継続的に進化していますが、私たちはまだ、テーブルがマークアップの王様で、セマンティクスが重要視されず、ブラウザーがページをレンダリングする方法についての標準がなかった暗い過去から立ち直っているところです。
これらの新しい標準を推進することは重要ですが、開発要件によっては、ベンダー プレフィックスを処理したり、さまざまなブラウザーや OS でテストしたり、サードパーティのツールに依存してこれらの違いを適切に識別したりすることで、このような移行に適応する必要がある場合もあります。
私たちにできるのは、W3C 仕様が厳密に改訂され、ブラウザ開発チームによって厳密に実装され、いつか私たち全員が使用できる共通標準が確立される未来を願うことだけです。
Page Visibility API に関しては、George Berkeley の言葉を引用して次のように述べます。
「見える」ということは、認識されるということです。