有没有办法检测浏览器窗口当前是否处于活动状态?

Luk*_*ncl 560 javascript browser focus window

我有定期执行活动的JavaScript.当用户没有看到该站点时(即窗口或选项卡没有焦点),最好不要运行.

有没有办法使用JavaScript?

我的参考点:如果您使用的窗口未处于活动状态,则Gmail聊天会播放声音.

And*_*y E 664

自从最初撰写此答案以来,由于W3C ,新规范已达到推荐状态.在网页浏览权限API(在MDN)现在允许当一个页面被隐藏到用户我们更准确地检测.

目前的浏览器支持

  • Chrome 13+
  • Internet Explorer 10+
  • Firefox 10+
  • Opera 12.10+ [ 阅读笔记 ]

以下代码使用API​​,回退到不兼容的浏览器中不太可靠的模糊/焦点方法.

document.addEventListener("visibilitychange", onchange);
Run Code Online (Sandbox Code Playgroud)

onfocusin并且onfocusoutIE 9及更低版本必需的,而所有其他人都使用onfocusonblur使用onpageshow和的iOS除外onpagehide.

  • @AndyE您的解决方案似乎只有在用户更改选项卡或最小化/最大化窗口时才有效.但是,如果用户使选项卡处于活动状态,则不会触发onchange事件,但会从任务栏中最大化其他程序.那个场景有解决方案吗?谢谢! (8认同)
  • @AndyE我在铬上试过这个解决方案.如果我更改选项卡,它可以工作,但如果我更改窗口(ALT +选项卡)则不会.应该是?这是一个小提琴 - http://jsfiddle.net/8a9N6/17/ (5认同)
  • @JulienKronegg:这就是为什么我的答案特别提到了在我最初编写答案后进入工作草案状态的Page Visibility API.焦点/模糊方法为旧版浏览器提供有限的功能.绑定到其他事件,如在你的答案中,并不比这更多,并且更容易出现行为差异(例如当光标下方的窗口弹出时,IE不会触发鼠标输出).我建议更合适的操作是显示一条消息或图标,指示用户由于页面不活动而导致更新频率降低. (3认同)
  • @bellpeace:IE 应该将 `focusin` 和 `focusout` 从 iframe 传播到上层窗口。对于较新的浏览器,您只需要处理每个 iframe 的“window”对象上的“focus”和“blur”事件。您应该使用我刚刚添加的更新代码,它至少可以在较新的浏览器中涵盖这些情况。 (2认同)
  • @Heliodor:我现在想把代码保留在最小的答案中.它从来没有打算成为一个剪切和粘贴的完整解决方案,因为实现者可能希望避免在主体上设置类并完全采取完全不同的操作(例如停止和启动计时器). (2认同)

小智 126

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

$(window).blur(function(){
  //your code here
});
$(window).focus(function(){
  //your code
});
Run Code Online (Sandbox Code Playgroud)

或者至少它对我有用.

  • 这不再适用于当前版本的现代浏览器,请参阅已批准的答案(页面可见性API) (16认同)
  • 在 Firefox 中,如果您单击 firebug 控制台(在同一页面上),“窗口”将失去焦点,这是正确的,但根据您的意图可能不是您所需要的。 (2认同)

Jul*_*egg 44

有3种典型的方法用于确定用户是否可以看到HTML页面,但是它们都不能完美地工作:

  • W3C网页浏览权限API应该做到这一点(支持,因为:火狐10,MSIE 10,铬13).但是,此API仅在完全覆盖浏览器选项卡时(例如,当用户从一个选项卡更改为另一个选项卡时)引发事件.当无法以100%的准确度确定可见性时,API不会引发事件(例如,Alt + Tab切换到另一个应用程序).

  • 使用基于焦点/模糊的方法会给您带来很多误报.例如,如果用户在浏览器窗口顶部显示较小的窗口,则浏览器窗口将失去焦点(onblur凸起),但用户仍然能够看到它(因此仍需要刷新).另见http://javascript.info/tutorial/focus

  • 依靠用户活动(鼠标移动,点击,键入键)也会给你带来很多误报.考虑与上述相同的情况,或观看视频的用户.

为了改善上述不完美行为,我结合使用了3种方法:W3C Visibility API,然后是焦点/模糊和用户活动方法,以降低误报率.这允许管理以下事件:

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

这是它的工作原理:当文档松散焦点时,监视文档上的用户活动(例如鼠标移动)以确定窗口是否可见.页面可见性概率与页面上最后一次用户活动的时间成反比:如果用户长时间未对文档进行任何活动,则该页面很可能不可见.下面的代码模仿W3C页面可见性API:它的行为方式相同,但误报率很低.它具有多浏览器的优势(在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>

由于目前没有可用的跨浏览器解决方案而没有误报,因此您最好三思而后行,禁用网站上的定期活动.

  • @Jacob 我很高兴你喜欢我的解决方案。随意将其推广到 GitHub 项目中。我通过 https://creativecommons.org/licenses/by/4.0/ 提供了带有 Creative Commons 许可证的代码 (2认同)

omn*_*nom 25

GitHub上有一个整洁的库:

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

例:

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

我已经在我拥有的所有浏览器上测试了版本1.0.1,并且可以确认它适用于:

  • IE9,IE10
  • FF 26.0
  • Chrome 34.0

......可能还有所有新版本.

不完全适用于:

  • IE8 - 始终指示选项卡/窗口当前处于活动状态(.now()始终true为我返回)

  • 这个图书馆完全被废弃了。虽然看起来它有一个 TypeScript 版本,但它不再在 VSCode 中工作,甚至复制/粘贴源代码也有很多内容不再被认为是 TypeScript 的良好实践 (2认同)

小智 17

我为我的应用程序创建了Comet Chat,当我收到来自其他用户的消息时,我使用:

if(new_message){
    if(!document.hasFocus()){
        audio.play();
        document.title="Have new messages";
    }
    else{
        audio.stop();
        document.title="Application Name";
    } 
}
Run Code Online (Sandbox Code Playgroud)

  • `document.hasFocus()`是最干净的方法.使用基于可见性api或事件或查找各种级别的用户活动/缺乏活动的所有其他方式变得过于复杂并且充满边缘情况和漏洞.将它放在一个简单的间隔上,并在结果发生变化时引发自定义事件.示例:https://jsfiddle.net/59utucz6/1/ (4认同)
  • 最干净的解决方案,支持IE6 (2认同)

l2a*_*lba 17

使用: 页面可见性API

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

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

  • 我也不是在谈论 ide。我正在谈论 alt-tabbing/cmd-tabbing 到另一个应用程序。突然页面不活跃了。页面可见性 api 不能帮助我知道页面是否不活动,它只能帮助我知道页面是否可能不可见。 (2认同)

Dan*_*ter 12

我开始使用社区维基回答,但意识到它没有检测到Chrome中的alt-tab事件.这是因为它使用了第一个可用的事件源,在这种情况下,它是页面可见性API,在Chrome中似乎不跟踪alt-tabbing.

我决定稍微修改脚本以跟踪页面焦点更改的所有可能事件.这是一个你可以参与的功能:

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;
};
Run Code Online (Sandbox Code Playgroud)

像这样使用它:

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


adr*_*anu 11

这适用于所有现代浏览器:

  • 更改选项卡时
  • 更改窗口时(Alt+Tab)
  • 从任务栏最大化另一个程序时
var eventName;
var visible = true;
var propName = "hidden";
if (propName in document) eventName = "visibilitychange";
else if ((propName = "msHidden") in document) eventName = "msvisibilitychange";
else if ((propName = "mozHidden") in document) eventName = "mozvisibilitychange";
else if ((propName = "webkitHidden") in document) eventName = "webkitvisibilitychange";
if (eventName) document.addEventListener(eventName, handleChange);

if ("onfocusin" in document) document.onfocusin = document.onfocusout = handleChange; //IE 9
window.onpageshow = window.onpagehide = window.onfocus = window.onblur = handleChange;// Changing tab with alt+tab

// Initialize state if Page Visibility API is supported
if (document[propName] !== undefined) handleChange({ type: document[propName] ? "blur" : "focus" });

function handleChange(evt) {
  evt = evt || window.event;
  if (visible && (["blur", "focusout", "pagehide"].includes(evt.type) || (this && this[propName]))){
    visible = false;
    console.log("Out...")
  }
  else if (!visible && (["focus", "focusin", "pageshow"].includes(evt.type) || (this && !this[propName]))){
    visible = true;
    console.log("In...")
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 这工作得非常好,即使打开开发工具它也会触发它,很棒的解决方案! (2认同)
  • 好技巧!这个应该更高。 (2认同)

El *_*mer 8

这在 chrome 67、firefox 67 上对我有用,

if(!document.hasFocus()) {
    // do stuff
}
Run Code Online (Sandbox Code Playgroud)


Ton*_*ada 7

这真的很棘手.鉴于以下要求,似乎没有解决方案.

  • 该页面包含您无法控制的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:YES
  • 4)当iframe有焦点时alt + tab:NO < - 无赖

当iframe具有焦点时,您的模糊/焦点事件根本不会被调用,并且页面Visibility API将不会在alt + tab上触发.

我在@建AndyE的解决方案,并在这里实现这个(差不多好了)的解决方案: https://dl.dropboxusercontent.com/u/2683925/estante-components/visibility_test1.html (对不起,我遇到了一些麻烦的jsfiddle).

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

这适用于铬/铬.它类似于firefox,除了它不加载iframe内容(任何想法为什么?)

无论如何,要解决最后一个问题(4),你唯一能做的就是在iframe上监听模糊/焦点事件.如果您对iframe有一定的控制权,则可以使用postMessage API来执行此操作.

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

我还没有用足够的浏览器测试过这个.如果您可以找到有关这不起作用的更多信息,请在下面的评论中告诉我们.

  • 所有这些链接都是404s :( (2认同)

rob*_*les 5

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

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

看:

  • 我认为这与 BFCache 相关:当用户单击后退或前进时 - 它与位于计算机桌面顶部的页面无关。 (2认同)

yck*_*art 5

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);
});
Run Code Online (Sandbox Code Playgroud)

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


Sah*_*kar 5

这对我有用

document.addEventListener("visibilitychange", function() {
      document.title = document.hidden ? "I'm away" : "I'm here";
});
Run Code Online (Sandbox Code Playgroud)

演示:https : //iamsahilralkar.github.io/document-hidden-demo/