web-dev-qa-db-ja.com

ブラウザウィンドウが現在アクティブではないかどうかを検出する方法はありますか?

定期的に活動しているJavaScriptがあります。ユーザーがサイトを見ていないとき(つまり、ウィンドウやタブにフォーカスがないとき)は、実行しないのがいいでしょう。

JavaScriptを使用してこれを行う方法はありますか?

私の参照ポイント:あなたが使用しているウィンドウがアクティブでない場合、Gmailチャットは音を出します。

533
Luke Francl

もともとこの答えを書いて以来、新しい仕様はW3Cのおかげで推奨ステータスに達しました。 Page Visibility API (on MDN )を使用すると、ページがユーザーに非表示になるタイミングをより正確に検出できるようになりました。

現在のブラウザのサポート:

  • Chrome 13+
  • Internet Explorer 10以降
  • Firefox 10以降
  • オペラ12.10+ [ メモを読む ]

次のコードはAPIを利用していますが、互換性のないブラウザでは信頼性の低いぼかし/フォーカス方法に頼っています。

(function() {
  var hidden = "hidden";

  // Standards:
  if (hidden in document)
    document.addEventListener("visibilitychange", onchange);
  else if ((hidden = "mozHidden") in document)
    document.addEventListener("mozvisibilitychange", onchange);
  else if ((hidden = "webkitHidden") in document)
    document.addEventListener("webkitvisibilitychange", onchange);
  else if ((hidden = "msHidden") in document)
    document.addEventListener("msvisibilitychange", onchange);
  // IE 9 and lower:
  else if ("onfocusin" in document)
    document.onfocusin = document.onfocusout = onchange;
  // All others:
  else
    window.onpageshow = window.onpagehide
    = window.onfocus = window.onblur = onchange;

  function onchange (evt) {
    var v = "visible", h = "hidden",
        evtMap = {
          focus:v, focusin:v, pageshow:v, blur:h, focusout:h, pagehide:h
        };

    evt = evt || window.event;
    if (evt.type in evtMap)
      document.body.className = evtMap[evt.type];
    else
      document.body.className = this[hidden] ? "hidden" : "visible";
  }

  // set the initial state (but only if browser supports the Page Visibility API)
  if( document[hidden] !== undefined )
    onchange({type: document[hidden] ? "blur" : "focus"});
})();

onfocusinname__およびonfocusoutname__は IE 9以下に必要 ですが、他のすべてのものはonfocusname__およびonblurname__を使用しますが、iOSはonpageshowname__およびonpagehidename__を使用します。

634
Andy E

私がjQueryを使うのは、あなたがしなければならないのはこれだけだからです。

$(window).blur(function(){
  //your code here
});
$(window).focus(function(){
  //your code
});

または少なくともそれは私のために働いた。

124
Carson Wright

GitHubには素敵なライブラリがあります。

https://github.com/serkanyersen/ifvisible.js

例:

// If page is visible right now
if( ifvisible.now() ){
  // Display pop-up
  openPopUp();
}

私は私が持っているすべてのブラウザでバージョン1.0.1をテストし、それが動作することを確認することができます:

  • IE9、IE10
  • FF 26.0
  • Chrome 34.0

...そしておそらくすべての新しいバージョンです。

完全には動作しません。

  • IE8 - 常にタブ/ウィンドウがアクティブであることを常に示す(.now()は常にtrueを返す)
23
omnomnom

私は自分のアプリ用にComet Chatを作成し、他のユーザーからメッセージを受け取ったときには次のものを使用します。

if(new_message){
    if(!document.hasFocus()){
        audio.play();
        document.title="Have new messages";
    }
    else{
        audio.stop();
        document.title="Application Name";
    } 
}
13
infinito84

ページ表示API を使用する

document.addEventListener( 'visibilitychange' , function() {
    if (document.hidden) {
        console.log('bye');
    } else {
        console.log('well back');
    }
}, false );

使用できますか?http://caniuse.com/#feat=pagevisibility

13
l2aelba

私はコミュニティWikiの答えを使って始めましたが、それがChromeの代替タブイベントを検出していないことに気付きました。これは、最初に利用可能なイベントソースを使用しているためです。この場合は、ページ可視化APIです。Chromeでは、Alt + Tabキーを追跡できないようです。

私はスクリプトを少し修正して、すべてのページフォーカスの変更に関する可能性のあるイベントを追跡することにしました。これはあなたが入れることができる関数です:

function onVisibilityChange(callback) {
    var visible = true;

    if (!callback) {
        throw new Error('no callback given');
    }

    function focused() {
        if (!visible) {
            callback(visible = true);
        }
    }

    function unfocused() {
        if (visible) {
            callback(visible = false);
        }
    }

    // Standards:
    if ('hidden' in document) {
        document.addEventListener('visibilitychange',
            function() {(document.hidden ? unfocused : focused)()});
    } else if ('mozHidden' in document) {
        document.addEventListener('mozvisibilitychange',
            function() {(document.mozHidden ? unfocused : focused)()});
    } else if ('webkitHidden' in document) {
        document.addEventListener('webkitvisibilitychange',
            function() {(document.webkitHidden ? unfocused : focused)()});
    } else if ('msHidden' in document) {
        document.addEventListener('msvisibilitychange',
            function() {(document.msHidden ? unfocused : focused)()});
    } else if ('onfocusin' in document) {
        // IE 9 and lower:
        document.onfocusin = focused;
        document.onfocusout = unfocused;
    } else {
        // All others:
        window.onpageshow = window.onfocus = focused;
        window.onpagehide = window.onblur = unfocused;
    }
};

このように使用してください。

onVisibilityChange(function(visible) {
    console.log('the page is now', visible ? 'focused' : 'unfocused');
});
10

これは本当にトリッキーです。以下の要件を考慮した解決策はないようです。

  • このページには、あなたが管理できないiframeが含まれています。
  • TABの変更(Ctrl + Tab)またはウィンドウの変更(Alt + Tab)によって引き起こされた変更に関係なく、表示状態の変更を追跡します。

これは次のような理由で起こります。

  • ページのVisibility APIはタブの変更を確実に伝えることができます(iframeがあっても)が、ユーザーがウィンドウを変更したときにはわかりません。
  • ウィンドウのぼかし/フォーカスイベントをリスニングすると、iframeにフォーカスがない限り、alt +タブとctrl +タブを検出できます。

これらの制限を考えると、 - ページの可視性API - ウィンドウのぼかし/フォーカス - document.activeElementを組み合わせたソリューションを実装することが可能です。

それは可能です:

  • 1)親ページにフォーカスがあるときにCtrl + Tabキー:YES
  • 2)iframeにフォーカスがあるときはCtrl + Tab:YES
  • 3)親ページにフォーカスがあるときにAlt + Tabキー:はい
  • 4)iframeにフォーカスがあるときにAlt + Tabキー:NO< - 残念

Iframeにフォーカスがあると、blur/focusイベントはまったく呼び出されず、Visibility APIページはalt +タブではトリガーされません。

私は@ AndyEのソリューションを基にして、この(ほぼ良い)ソリューションをここに実装しました。 https://dl.dropboxusercontent.com/u/2683925/estante-components/visibility_test1.html (申し訳ありません) 、JSFiddleに問題がありました。

これはGithubでも利用できます。 https://github.com/qmagico/estante-components

これはクロム/クロムに作用します。それは、iframeの内容をロードしないことを除いて、Firefox上でも同様に機能します(何故か?)

とにかく、最後の問題(4)を解決する唯一の方法は、iframeのぼかし/フォーカスイベントを監視することです。 iframeをある程度制御できる場合は、postMessage APIを使用してそれを実行できます。

https://dl.dropboxusercontent.com/u/2683925/estante-components/visibility_test2.html

私はまだ十分なブラウザでこれをテストしていません。あなたがこれがうまくいかない場所についてのより多くの情報を見つけることができるならば、下のコメントで私に知らせてください。

7
Tony Lâmpada
var visibilityChange = (function (window) {
    var inView = false;
    return function (fn) {
        window.onfocus = window.onblur = window.onpageshow = window.onpagehide = function (e) {
            if ({focus:1, pageshow:1}[e.type]) {
                if (inView) return;
                fn("visible");
                inView = true;
            } else if (inView) {
                fn("hidden");
                inView = false;
            }
        };
    };
}(this));

visibilityChange(function (state) {
    console.log(state);
});

http://jsfiddle.net/ARTsinn/JTxQY/

4
yckart

HTML 5では、次のものも使えます。

  • onpageshow:ウィンドウが見えるようになったときに実行されるスクリプト
  • onpagehide:ウィンドウが隠れているときに実行されるスクリプト

見る:

3
roberkules

これは私にとってChrome 67、Firefox 67、で機能します。

if(!document.hasFocus()) {
    // do stuff
}
3
El Pr0grammer

あなたが使用することができます:

(function () {

    var requiredResolution = 10; // ms
    var checkInterval = 1000; // ms
    var tolerance = 20; // percent


    var counter = 0;
    var expected = checkInterval / requiredResolution;
    //console.log('expected:', expected);

    window.setInterval(function () {
        counter++;
    }, requiredResolution);

    window.setInterval(function () {
        var deviation = 100 * Math.abs(1 - counter / expected);
        // console.log('is:', counter, '(off by', deviation , '%)');
        if (deviation > tolerance) {
            console.warn('Timer resolution not sufficient!');
        }
        counter = 0;
    }, checkInterval);

})();
3
maryam

これはAndy Eからの回答の修正です。

これはタスクを行います。ページが表示されてフォーカスされている場合に限り、30秒ごとにページを更新します。

視認性が検出できない場合は、焦点のみが使用されます。

ユーザーがページにフォーカスを合わせた場合は、すぐに更新されます

Ajaxの呼び出しから30秒が経過するまでページは更新されません

var windowFocused = true;
var timeOut2 = null;

$(function(){
  $.ajaxSetup ({
    cache: false
  });
  $("#content").ajaxComplete(function(event,request, settings){
       set_refresh_page(); // ajax call has just been made, so page doesn't need updating again for 30 seconds
   });
  // check visibility and focus of window, so as not to keep updating unnecessarily
  (function() {
      var hidden, change, vis = {
              hidden: "visibilitychange",
              mozHidden: "mozvisibilitychange",
              webkitHidden: "webkitvisibilitychange",
              msHidden: "msvisibilitychange",
              oHidden: "ovisibilitychange" /* not currently supported */
          };
      for (hidden in vis) {
          if (vis.hasOwnProperty(hidden) && hidden in document) {
              change = vis[hidden];
              break;
          }
      }
      document.body.className="visible";
      if (change){     // this will check the tab visibility instead of window focus
          document.addEventListener(change, onchange,false);
      }

      if(navigator.appName == "Microsoft Internet Explorer")
         window.onfocus = document.onfocusin = document.onfocusout = onchangeFocus
      else
         window.onfocus = window.onblur = onchangeFocus;

      function onchangeFocus(evt){
        evt = evt || window.event;
        if (evt.type == "focus" || evt.type == "focusin"){
          windowFocused=true; 
        }
        else if (evt.type == "blur" || evt.type == "focusout"){
          windowFocused=false;
        }
        if (evt.type == "focus"){
          update_page();  // only update using window.onfocus, because document.onfocusin can trigger on every click
        }

      }

      function onchange () {
        document.body.className = this[hidden] ? "hidden" : "visible";
        update_page();
      }

      function update_page(){
        if(windowFocused&&(document.body.className=="visible")){
          set_refresh_page(1000);
        }
      }


  })();
  set_refresh_page();
})

function get_date_time_string(){
  var d = new Date();
  var dT = [];
  dT.Push(d.getDate());
  dT.Push(d.getMonth())
  dT.Push(d.getFullYear());
  dT.Push(d.getHours());
  dT.Push(d.getMinutes());
  dT.Push(d.getSeconds());
  dT.Push(d.getMilliseconds());
  return dT.join('_');
}

function do_refresh_page(){

// do tasks here

// e.g. some ajax call to update part of the page.

// (date time parameter will probably force the server not to cache)

//      $.ajax({
//        type: "POST",
//        url: "someUrl.php",
//        data: "t=" + get_date_time_string()+"&task=update",
//        success: function(html){
//          $('#content').html(html);
//        }
//      });

}

function set_refresh_page(interval){
  interval = typeof interval !== 'undefined' ? interval : 30000; // default time = 30 seconds
  if(timeOut2 != null) clearTimeout(timeOut2);
  timeOut2 = setTimeout(function(){
    if((document.body.className=="visible")&&windowFocused){
      do_refresh_page();
    }
    set_refresh_page();
  }, interval);
}
2
roger

Angular.jsの場合、これはあなたのコントローラが可視性の変化に反応することを可能にする(受け入れられた答えに基づく)ディレクティブです:

myApp.directive('reactOnWindowFocus', function($parse) {
    return {
        restrict: "A",
        link: function(scope, element, attrs) {
            var hidden = "hidden";
            var currentlyVisible = true;
            var functionOrExpression = $parse(attrs.reactOnWindowFocus);

          // Standards:
          if (hidden in document)
            document.addEventListener("visibilitychange", onchange);
          else if ((hidden = "mozHidden") in document)
            document.addEventListener("mozvisibilitychange", onchange);
          else if ((hidden = "webkitHidden") in document)
            document.addEventListener("webkitvisibilitychange", onchange);
          else if ((hidden = "msHidden") in document)
            document.addEventListener("msvisibilitychange", onchange);
          else if ("onfocusin" in document) {
                // IE 9 and lower:
            document.onfocusin = onshow;
                document.onfocusout = onhide;
          } else {
                // All others:
            window.onpageshow = window.onfocus = onshow;
                window.onpagehide = window.onblur = onhide;
            }

          function onchange (evt) {
                //occurs both on leaving and on returning
                currentlyVisible = !currentlyVisible;
                doSomethingIfAppropriate();
          }

            function onshow(evt) {
                //for older browsers
                currentlyVisible = true;
                doSomethingIfAppropriate();
            }

            function onhide(evt) {
                //for older browsers
                currentlyVisible = false;
                doSomethingIfAppropriate();
            }

            function doSomethingIfAppropriate() {
                if (currentlyVisible) {
                    //trigger angular digest cycle in this scope
                    scope.$apply(function() {
                        functionOrExpression(scope);
                    });
                }
            }
        }
    };

});

この例のように使用することができます:<div react-on-window-focus="refresh()">、ここでrefresh()はControllerがスコープ内にあるものすべてのスコープ内のスコープ関数です。

1
Steve Campbell

JQueryを使わない解決策については、3つのページ状態に関する情報を提供する Visibility.js をチェックしてください。

visible    ... page is visible
hidden     ... page is not visible
prerender  ... page is being prerendered by the browser

setIntervalの便利なラッパー

/* Perform action every second if visible */
Visibility.every(1000, function () {
    action();
});

/* Perform action every second if visible, every 60 sec if not visible */
Visibility.every(1000, 60*1000, function () {
    action();
});

古いブラウザ(IE <10、iOS <7)への代替も可能です。

1
Niko

もう少し複雑な方法はsetInterval()を使ってマウスの位置をチェックし、最後のチェックと比較することです。マウスが設定された時間内に動かなかった場合、ユーザーはおそらくアイドル状態です。

これは、ウィンドウがアクティブでないかどうかをjustでチェックするのではなく、ユーザーがアイドル状態かどうかを判断するという利点があります。

多くの人が指摘しているように、ユーザーがマウスを使用していない、ビデオを見ていないなどの理由で、ユーザーウィンドウまたはブラウザウィンドウがアイドル状態かどうかを確認するにはこれが必ずしも良い方法とは言えません。私はただアイドル状態をチェックする一つの可能​​な方法を提案しています。

1
Austin Hyde

ただ追加したいと思いました:質問は明確に書かれていません。 「ユーザーがサイトを見ていないとき(つまり、ウィンドウまたはタブにフォーカスがないとき)...」

焦点が合っていないサイトを見ることができます。ほとんどのデスクトップシステムはウィンドウを並行して表示することができます:)

ページ可視化APIはおそらく「ユーザーが更新を見ることができない」場合には「タブにフォーカスがない」とは大きく異なる場合があるため、サイトの更新を妨げるため、正しい答えであると考えられます。

0
HolgerJeromin

onwholeブラウザぼかしのように動作したい場合:ブラウザのルーズフォーカスがない場合は推奨されないイベントが発生します。私の考えは、イベントが発生した場合、ループでカウントアップし、カウンターをリセットすることです。カウンターが限界に達したら私は他のページにlocation.hrefをする。 dev-toolsで作業している場合にも発生します。

var iput=document.getElementById("hiddenInput");
   ,count=1
   ;
function check(){
         count++;
         if(count%2===0){
           iput.focus();
         }
         else{
           iput.blur();
         }
         iput.value=count;  
         if(count>3){
           location.href="http://Nirwana.com";
         }              
         setTimeout(function(){check()},1000);
}   
iput.onblur=function(){count=1}
iput.onfocus=function(){count=1}
check();

これはFFでテストが成功したドラフトです。

0
B.F.