我有JavaScript,它周期性地执行活动。当用户没有在看站点时(例如,窗口或选项卡没有焦点),最好不要运行。

是否有一种方法可以使用JavaScript来做到这一点?

我的参考点:如果你正在使用的窗口不活跃,Gmail聊天就会播放声音。


当前回答

有3种典型的方法来确定用户是否可以看到HTML页面,但是没有一种方法是完美的:

The W3C Page Visibility API is supposed to do this (supported since: Firefox 10, MSIE 10, Chrome 13). However, this API only raises events when the browser tab is fully overriden (e.g. when the user changes from one tab to another one). The API does not raise events when the visibility cannot be determined with 100% accuracy (e.g. Alt+Tab to switch to another application). Using focus/blur based methods gives you a lot of false positive. For example, if the user displays a smaller window on top of the browser window, the browser window will lose the focus (onblur raised) but the user is still able to see it (so it still need to be refreshed). See also http://javascript.info/tutorial/focus Relying on user activity (mouse move, clicks, key typed) gives you a lot of false positive too. Think about the same case as above, or a user watching a video.

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

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

This is how it works: when the document lose the focus, the user activity (such as mouse move) on the document is monitored in order to determine if the window is visible or not. The page visibility probability is inversely proportional to the time of the last user activity on the page: if the user makes no activity on the document for a long time, the page is most probably not visible. The code below mimics the W3C Page Visibility API: it behaves the same way but has a small false positive rate. It has the advantage to be multibrowser (tested on Firefox 5, Firefox 10, MSIE 9, MSIE 7, Safari 5, Chrome 9).


    <div id="x"></div>
     
    <script>
    /**
    Registers the handler to the event for the given object.
    @param obj the object which will raise the event
    @param evType the event type: click, keypress, mouseover, ...
    @param fn the event handler function
    @param isCapturing set the event mode (true = capturing event, false = bubbling event)
    @return true if the event handler has been attached correctly
    */
    function addEvent(obj, evType, fn, isCapturing){
      if (isCapturing==null) isCapturing=false; 
      if (obj.addEventListener){
        // Firefox
        obj.addEventListener(evType, fn, isCapturing);
        return true;
      } else if (obj.attachEvent){
        // MSIE
        var r = obj.attachEvent('on'+evType, fn);
        return r;
      } else {
        return false;
      }
    }
     
    // register to the potential page visibility change
    addEvent(document, "potentialvisilitychange", function(event) {
      document.getElementById("x").innerHTML+="potentialVisilityChange: potentialHidden="+document.potentialHidden+", document.potentiallyHiddenSince="+document.potentiallyHiddenSince+" s<br>";
    });
     
    // register to the W3C Page Visibility API
    var hidden=null;
    var visibilityChange=null;
    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";
    } else if (typeof document.hidden !=="hidden") {
      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, // in seconds
      init:function() {
        function setAsNotHidden() {
          var dispatchEventRequired=document.potentialHidden;
          document.potentialHidden=false;
          document.potentiallyHiddenSince=0;
          if (dispatchEventRequired) dispatchPageVisibilityChangeEvent();
        }
     
        function initPotentiallyHiddenDetection() {
          if (!hasFocusLocal) {
            // the window does not has the focus => check for  user activity in the window
            lastActionDate=new Date();
            if (timeoutHandler!=null) {
              clearTimeout(timeoutHandler);
            }
            timeoutHandler = setTimeout(checkPageVisibility, potentialPageVisibility.pageVisibilityChangeThreshold*1000+100); // +100 ms to avoid rounding issues under Firefox
          }
        }
     
        function dispatchPageVisibilityChangeEvent() {
          unifiedVisilityChangeEventDispatchAllowed=false;
          var evt = document.createEvent("Event");
          evt.initEvent("potentialvisilitychange", true, true);
          document.dispatchEvent(evt);
        }
     
        function checkPageVisibility() {
          var potentialHiddenDuration=(hasFocusLocal || lastActionDate==null?0:Math.floor((new Date().getTime()-lastActionDate.getTime())/1000));
                                        document.potentiallyHiddenSince=potentialHiddenDuration;
          if (potentialHiddenDuration>=potentialPageVisibility.pageVisibilityChangeThreshold && !document.potentialHidden) {
            // page visibility change threshold raiched => raise the even
            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:<br>";
        });
        addEvent(document, "pagehide", function(event) {
          document.getElementById("x").innerHTML+="pagehide/doc:<br>";
        });
        addEvent(window, "pageshow", function(event) {
          document.getElementById("x").innerHTML+="pageshow/win:<br>"; // raised when the page first shows
        });
        addEvent(window, "pagehide", function(event) {
          document.getElementById("x").innerHTML+="pagehide/win:<br>"; // not raised
        });
        addEvent(document, "mousemove", function(event) {
          lastActionDate=new Date();
        });
        addEvent(document, "mouseover", function(event) {
          hasMouseOver=true;
          setAsNotHidden();
        });
        addEvent(document, "mouseout", function(event) {
          hasMouseOver=false;
          initPotentiallyHiddenDetection();
        });
        addEvent(window, "blur", function(event) {
          hasFocusLocal=false;
          initPotentiallyHiddenDetection();
        });
        addEvent(window, "focus", function(event) {
          hasFocusLocal=true;
          setAsNotHidden();
        });
        setAsNotHidden();
      }
    }
     
    potentialPageVisibility.pageVisibilityChangeThreshold=4; // 4 seconds for testing
    potentialPageVisibility.init();
    </script>

由于目前还没有有效的跨浏览器解决方案,所以在禁用网站上的周期性活动时最好三思而后行。

其他回答

使用:页面可见性API

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

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

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

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

See:

https://developer.mozilla.org/en-US/docs/Web/Events/pageshow https://developer.mozilla.org/en-US/docs/Web/Events/pagehide

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

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

至少对我有用。

对于没有jQuery的解决方案,请参阅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)也可用

如果你想对整个浏览器模糊: 正如我所评论的,如果浏览器失去焦点,建议的事件都不会触发。我的想法是在循环中计数,并在事件发生时重置计数器。如果计数器达到限制,我做一个位置。请转到另一页。如果你在开发工具上工作,这也会触发。

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上成功测试的草稿。