有没有办法来检测浏览器窗口是否目前不活动?

我有定期做活动的JavaScript。 当用户没有看到该网站(即窗口或选项卡没有焦点),这将是很好的不运行。

有没有办法做到这一点使用JavaScript?

我的参考点:如果您使用的窗口未激活,则Gmail聊天会播放声音。

自从最初写这个答案以来,由于W3C,一个新的规范已经达到了推荐的地位。 页面可见性API (在MDN上 )现在允许我们更准确地检测页面何时隐藏给用户。

当前浏览器支持:

  • Chrome 13+
  • Internet Explorer 10+
  • Firefox 10+
  • Opera 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"}); })(); 

onfocusinonfocusout 对于IE 9和更低 onfocusout是必需的 ,而所有其他的则使用onfocusonblur ,除了使用onpageshowonpagehide iOS。

我会使用jQuery,因为那么你所要做的就是这样:

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

或者至less它为我工作。

有三种典型的方法来确定用户是否可以看到HTML页面,但是没有一个能够完美地工作:

  • W3C页面可见性API应该是这样做的(从以下版本开始支持:Firefox 10,MSIE 10,Chrome 13)。 但是,此API仅在浏览器选项卡完全覆盖时引发事件(例如,当用户从一个选项卡更改为另一个选项卡时)。 当无法以100%的准确度确定可见性时,API不会引发事件(例如,Alt + Tab切换到另一个应用程序)。

  • 使用基于焦点/模糊的方法会带来很多误报。 例如,如果用户在浏览器窗口顶部显示一个较小的窗口,那么浏览器窗口将失去焦点( onblur引发),但用户仍然可以看到它(所以仍然需要刷新)。 另见http://javascript.info/tutorial/focus

  • 依靠用户活动 (鼠标移动,点击,键入)也会给你很多误报。 想想上面的情况,或者观看video的用户。

为了改善上述不完美的行为,我使用了W3C可见性API,然后使用焦点/模糊和用户活动方法这三种方法的组合来降低误报率。 这允许pipe理以下事件:

  • 将浏览器选项卡更改为另一个(100%的准确性,这要归功于W3C页面可见性API)
  • 页面可能被另一个窗口隐藏,例如由于Alt + Tab(概率=不是100%准确)
  • 用户注意力可能不集中在HTML页面(概率=不是100%准确)

这是如何工作的:当文档失去焦点时,监视文档上的用户活动(例如鼠标移动)以确定窗口是否可见。 页面可见性概率与页面上最后一次用户活动的时间成反比:如果用户长时间不在文档上进行任何活动,页面可能不可见。 下面的代码模仿了W3C页面可见性API:它的行为方式相同,但误报率很小。 它具有多浏览器的优点(在Firefox 5,Firefox 10,MSIE 9,MSIE 7,Safari 5,Chrome 9上testing)。


     <div id =“x”> </ div>

     <SCRIPT>
     / **
    将处理程序注册到给定对象的事件。
     @param obj将会引发事件的对象
     @param evType事件types:点击,按键,鼠标hover,...
    事件处理函数@参数
     @param isCapturing设置事件模式(true =捕获事件,false =冒泡事件)
     @return如果事件处理程序已正确连接,则返回true
     * /
     function addEvent(obj,evType,fn,isCapturing){
       if(isCapturing == null)isCapturing = false; 
       if(obj.addEventListener){
         // Firefox
         obj.addEventListener(evType,fn,isCapturing);
        返回true;
       } else if(obj.attachEvent){
         // MSIE
         var r = obj.attachEvent('on'+ evType,fn);
        返回r;
       } else {
        返回false;
       }
     }

     //注册到潜在的页面可见性变化
     addEvent(document,“potentialvisilitychange”,function(event){
       document.potEtiallyHiddenSince =“+ document.potentiallyHiddenSince +”s“; document.getElementById(”x“)。innerHTML + =”potentialVisilityChange:potentialHidden =“+ document.potentialHidden +”,document.potentiallyHiddenSince =“+
     });

     //注册到W3C页面可见性API
     var hidden = null;
     var visibilityChange = null;
     if(typeof document.mozHidden!==“undefined”){
      隐藏= “mozHidden”;
       visibilityChange = “mozvisibilitychange”;
     } else if(typeof document.msHidden!==“undefined”){
      隐藏= “msHidden”;
       visibilityChange = “msvisibilitychange”;
     } else if(typeof document.webkitHidden!==“undefined”){
      隐藏= “webkitHidden”;
       visibilityChange = “webkitvisibilitychange”;
    其他如果(typeof document.hidden!==“hidden”){
      隐藏=“隐藏”;
       visibilityChange = “visibilitychange”;
     }
     if(hidden!= null && visibilityChange!= null){
       addEvent(document,visibilityChange,function(event){
         document.getElementById(“x”)。innerHTML + = visibilityChange +“:”+ hidden +“=”+ document [hidden] +“<br>”;
       });
     }


     var potentialPageVisibility = {
       pageVisibilityChangeThreshold:3 * 3600,//以秒为单位
       init:function(){
        函数setAsNotHidden(){
           var dispatchEventRequired = document.potentialHidden;
           document.potentialHidden = FALSE;
           document.potentiallyHiddenSince = 0;
           if(dispatchEventRequired)dispatchPageVisibilityChangeEvent();
         }

        函数initPotentiallyHiddenDetection(){
          如果(!hasFocusLocal){
             //窗口没有焦点=>检查窗口中的用户活动
             lastActionDate = new Date();
             if(timeoutHandler!= null){
               clearTimeout(timeoutHandler);
             }
             timeoutHandler = setTimeout(checkPageVisibility,potentialPageVisibility.pageVisibilityChangeThreshold * 1000 + 100);  // + 100毫秒,以避免在Firefox下取整问题
           }
         }

        函数dispatchPageVisibilityChangeEvent(){
           unifiedVisilityChangeEventDispatchAllowed = FALSE;
           var evt = document.createEvent(“Event”);
           evt.initEvent(“potentialvisilitychange”,true,true);
           document.dispatchEvent(EVT);
         }

        函数checkPageVisibility(){
           var potentialHiddenDuration =(hasFocusLocal || lastActionDate == null?0:Math.floor((new Date()。getTime() -  lastActionDate.getTime())/ 1000));
                                         document.potentiallyHiddenSince = potentialHiddenDuration;
           if(potentialHiddenDuration> = potentialPageVisibility.pageVisibilityChangeThreshold &&!document.potentialHidden){
             //页面可见性变化阈值raiched =>提高均匀度
             document.potentialHidden = TRUE;
             dispatchPageVisibilityChangeEvent();
           }
         }

         var lastActionDate = null;
         var hasFocusLocal = true;
         var hasMouseOver = true;
         document.potentialHidden = FALSE;
         document.potentiallyHiddenSince = 0;
         var timeoutHandler = null;

         addEvent(document,“pageshow”,function(event){
          的document.getElementById( “X”)的innerHTML + = “pageshow / DOC:点击”。
         });
         addEvent(document,“pagehide”,function(event){
          的document.getElementById( “X”)的innerHTML + = “pagehide / DOC:点击”。
         });
         addEvent(窗口,“pageshow”,函数(事件){
           。的document.getElementById( “X”)的innerHTML + = “pageshow /赢:点击”;  //在页面首次显示时引发
         });
         addEvent(窗口,“pagehide”,函数(事件){
           。的document.getElementById( “X”)的innerHTML + = “pagehide /赢:点击”;  //没有提出
         });
         addEvent(document,“mousemove”,function(event){
           lastActionDate = new Date();
         });
         addEvent(文档,“mouseover”,函数(事件){
           hasMouseOver = TRUE;
           setAsNotHidden();
         });
         addEvent(document,“mouseout”,function(event){
           hasMouseOver = FALSE;
           initPotentiallyHiddenDetection();
         });
         addEvent(窗口,“模糊”,function(事件){
           hasFocusLocal = FALSE;
           initPotentiallyHiddenDetection();
         });
         addEvent(窗口,“焦点”,function(事件){
           hasFocusLocal = TRUE;
           setAsNotHidden();
         });
         setAsNotHidden();
       }
     }

     potentialPageVisibility.pageVisibilityChangeThreshold = 4;  //testing4秒
     potentialPageVisibility.init();
     </ SCRIPT>

由于目前没有正确的跨浏览器解决scheme,您最好在您的网站上重新考虑禁用定期活动。

在GitHub上有一个整洁的库:

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

例:

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

我已经在所有浏览器上testing过版本1.0.1,并且可以确认它适用于:

  • IE9,IE10
  • FF 26.0
  • Chrome 34.0

…也许所有更新的版本。

不完全适用于:

  • IE8 – 总是指示标签页/窗口当前处于活动状态( .now()对我来说总是返回true

这真的很棘手。 鉴于以下要求,似乎没有解决scheme。

  • 该页面包含您无法控制的iframe
  • 无论TAB更改(ctrl + tab)还是窗口更改(alt + tab)触发的更改都要跟踪可见性状态更改

这是因为:

  • 页面可见性API可以可靠地告诉你一个选项卡的变化(即使是在使用iframe的情况下),但是当用户改变窗口时它不能告诉你。
  • 只要iframe没有焦点,听窗口模糊/焦点事件就可以检测到alt +选项卡和ctrl +选项卡。

鉴于这些限制,可以实现一个解决scheme,它结合了 – 页面可见性API – 窗口模糊/焦点 – document.activeElement

这是能够:

  • 1)当父页面有焦点时,按Ctrl +标签:是
  • 2)当iframe具有焦点时,按Ctrl + Tab:YES
  • 3)当父页面有焦点时,alt +标签:是
  • 4)当iframe有焦点时alt + tab: NO < – 无赖

当iframe具有焦点时,您的模糊/焦点事件根本不会被调用,并且页面可见性API将不会在alt +选项卡上触发。

我build立在@ AndyE的解决scheme,并在这里实现这个(几乎是好的)解决scheme: https ://dl.dropboxusercontent.com/u/2683925/estante-components/visibility_test1.html(对不起,我有一些JSFiddle的麻烦)。

这也可以在Github上find: https : //github.com/qmagico/estante-components

这适用于铬/铬。 它适用于Firefox,除了它不加载iframe的内容(任何想法为什么?)

无论如何,要解决最后一个问题(4),唯一的方法就是在iframe上侦听模糊/焦点事件。 如果你对iframe有一些控制,你可以使用postMessage API来做到这一点。

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

我还没有testing过足够多的浏览器。 如果你可以find更多的信息,这不起作用,请让我知道下面的评论。

我为我的应用程序创build一个彗星聊天,当我收到来自另一个用户的消息时,我使用:

 if(new_message){ if(!document.hasFocus()){ audio.play(); document.title="Have new messages"; } else{ audio.stop(); document.title="Application Name"; } } 
 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/

我开始使用社区维基答案,但意识到它没有在Chrome中检测到ALT键。 这是因为它使用了第一个可用的事件源,在这种情况下,它是页面可见性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)()}); } if ('mozHidden' in document) { document.addEventListener('mozvisibilitychange', function() {(document.mozHidden ? unfocused : focused)()}); } if ('webkitHidden' in document) { document.addEventListener('webkitvisibilitychange', function() {(document.webkitHidden ? unfocused : focused)()}); } if ('msHidden' in document) { document.addEventListener('msvisibilitychange', function() {(document.msHidden ? unfocused : focused)()}); } // IE 9 and lower: if ('onfocusin' in document) { document.onfocusin = focused; document.onfocusout = unfocused; } // All others: window.onpageshow = window.onfocus = focused; window.onpagehide = window.onblur = unfocused; }; 

像这样使用它:

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

使用: 页面可见性API

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

我可以用吗 ? http://caniuse.com/#feat=pagevisibility

你可以使用:

 (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); })(); 

稍微复杂一点的方法是使用setInterval()来检查鼠标的位置,并与上次检查进行比较。 如果鼠标没有在一定的时间内移动,用户可能是空闲的。

这具有额外的优点,即告诉用户是否空闲,而不是仅仅检查窗口是否处于活动状态。

正如很多人所指出的,这并不总是检查用户或浏览器窗口是否空闲的好方法,因为用户甚至可能不使用鼠标或正在观看video等。 我只是build议一种可能的方法来检查空闲状态。

这是对安迪的回答。

这将执行一项任务,例如每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 // eg 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); } 

在HTML 5中,您也可以使用:

  • onpageshow :当窗口变得可见时要运行的脚本
  • onpagehide :隐藏窗口时要运行的脚本

看到:

对于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的作用域函数。

对于没有jQuery的解决scheme,请查看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)也可以使用

如果你想整个浏览器模糊处理 :正如我评论的,如果浏览器松散焦点没有build议的事件触发。 我的想法是计数在一个循环,并重置计数器,如果事件发生。 如果计数器达到一个限制,我做一个location.href到另一个页面。 如果您使用开发工具,这也会引发火灾。

 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(); 

这是一个FFtesting成功的草稿。

只是想补充一点:这个问题不清楚写的。 “当用户没有看到该网站(即,窗口或标签没有焦点)…”

当没有焦点时,我可以看一个网站。 大多数桌面系统能够并行显示窗口:)

这就是为什么页面可见性API可能是正确的答案,因为当“用户无法看到更新”时,它会阻止更新站点,这与“选项卡没有焦点”非常不同。