【问题标题】:Is there a way to detect if a browser window is not currently active?有没有办法检测浏览器窗口当前是否处于活动状态?
【发布时间】:2009-06-29 19:20:00
【问题描述】:

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

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

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

【问题讨论】:

  • 如果对下面的答案不满意,请查看requestAnimationFrame API,或者使用现代功能,即在窗口未打开时减少setTimeout/setInterval 的频率可见(例如,在 Chrome 中为 1 秒)。
  • document.body.onblur=function(e){console.log('lama');} 适用于非焦点元素。
  • 有关使用 W3C 页面可见性 API 的跨浏览器兼容解决方案,请参阅 this answer,在不支持它的浏览器中回退到 blur/focus
  • 以下 80% 的答案不是这个问题的答案。该问题询问当前未激活,但下面的大量答案是关于不可见,这不是该问题的答案。可以说它们应该被标记为“不是答案”
  • 大多数人谈论不活跃时的意思是不活跃且不可见。简单地 不活动 很容易 - 只需处理窗口 blur/focus 事件...尽管使用有限,因为窗口可以不活动但完全或部分可见(还有人们希望继续更新的某些任务栏中的“预览”图标)。

标签: javascript browser focus window


【解决方案1】:

自从最初编写此答案以来,由于 W3C,新规范已达到推荐状态。 Page Visibility APIMDN)现在允许我们更准确地检测页面何时对用户隐藏。

document.addEventListener("visibilitychange", onchange);

当前浏览器支持:

  • Chrome 13+
  • Internet Explorer 10+
  • 火狐10+
  • Opera 12.10+ [read notes]

以下代码回退到不兼容浏览器中不太可靠的模糊/聚焦方法:

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

onfocusinonfocusoutrequired for IE 9 and lower,而所有其他人都使用 onfocusonblur,但 iOS 除外,它使用 onpageshowonpagehide

【讨论】:

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

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

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

或者至少它对我有用。

【讨论】:

  • 对我来说,在 iframe 中调用了两次
  • 在 Firefox 中,如果您在 firebug 控制台内单击(在同一页面上),window 将失去焦点,这是正确的,但取决于您的意图可能不是您所需要的。
  • 这不再适用于当前版本的现代浏览器,请参阅批准的答案(页面可见性 API)
  • 此解决方案不适用于 iPad 请使用“pageshow”事件
  • 页面加载时 BLUR 和 FOCUS 都会触发。当我从我的页面打开一个新窗口时,什么也没有发生,但是一旦新窗口关闭,两个事件都会触发:/(使用 IE8)
【解决方案3】:

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

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

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

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

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

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

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

/** 将处理程序注册到给定对象的事件。 @param obj 将引发事件的对象 @param evType 事件类型:单击,按键,鼠标悬停,... @param fn 事件处理函数 @param isCapturing 设置事件模式(true = 捕获事件,false = 冒泡事件) @return 如果事件处理程序已正确附加,则返回 true */ 函数 addEvent(obj, evType, fn, isCapturing){ if (isCapturing==null) isCapturing=false; 如果(obj.addEventListener){ // 火狐 obj.addEventListener(evType, fn, isCapturing); 返回真; } 否则如果(obj.attachEvent){ // MSIE var r = obj.attachEvent('on'+evType, fn); 返回 r; } 别的 { 返回假; } } // 注册到潜在的页面可见性变化 addEvent(document, "potentialvisilitychange", function(event) { document.getElementById("x").innerHTML+="potentialVisilityChange: potentialHidden="+document.potentialHidden+", document.potentiallyHiddenSince="+document.potentiallyHiddenSince+" s
"; }); // 注册到 W3C Page Visibility API 变种隐藏=空; 变量可见性更改=空; 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") { 隐藏=“隐藏”; 可见性改变=“可见性改变”; } if (hidden!=null && visibilityChange!=null) { addEvent(document, visibilityChange, function(event) { document.getElementById("x").innerHTML+=visibilityChange+": "+hidden+"="+document[hidden]+"
"; }); } var potentialPageVisibility = { pageVisibilityChangeThreshold:3*3600, // 以秒为单位 初始化:函数(){ 函数 setAsNotHidden() { var dispatchEventRequired=document.potentialHidden; document.potentialHidden=false; document.potentiallyHiddenSince=0; if (dispatchEventRequired) dispatchPageVisibilityChangeEvent(); } 函数 initPotentiallyHiddenDetection() { 如果(!hasFocusLocal){ // 窗口没有焦点 => 检查窗口中的用户活动 lastActionDate=新日期(); if (timeoutHandler!=null) { 清除超时(超时处理程序); } timeoutHandler = setTimeout(checkPageVisibility, potentialPageVisibility.pageVisibilityChangeThreshold*1000+100); // +100 ms 以避免 Firefox 下的舍入问题 } } 函数 dispatchPageVisibilityChangeEvent() { 统一的VisilityChangeEventDispatchAllowed=false; var evt = document.createEvent("事件"); 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) { // 页面可见性更改阈值达到 => 提高偶数 document.potentialHidden=true; dispatchPageVisibilityChangeEvent(); } } var lastActionDate=null; var hasFocusLocal=true; 变量 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(窗口,“页面显示”,函数(事件){ document.getElementById("x").innerHTML+="pageshow/win:
"; // 在页面首次显示时引发 }); addEvent(窗口,“页面隐藏”,函数(事件){ document.getElementById("x").innerHTML+="pagehide/win:
"; // 未提出 }); addEvent(文档,“mousemove”,函数(事件){ lastActionDate=新日期(); }); addEvent(文档,“鼠标悬停”,函数(事件){ hasMouseOver=真; setAsNotHidden(); }); addEvent(文档,“mouseout”,函数(事件){ hasMouseOver=假; initPotentiallyHiddenDetection(); }); addEvent(窗口,“模糊”,函数(事件){ hasFocusLocal=false; initPotentiallyHiddenDetection(); }); addEvent(窗口,“焦点”,函数(事件){ hasFocusLocal=true; setAsNotHidden(); }); setAsNotHidden(); } } 潜在的PageVisibility.pageVisibilityChangeThreshold=4; // 4 秒测试 潜在的PageVisibility.init(); 脚本>

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

【讨论】:

  • 不会对字符串 'undefined' 使用严格的比较运算符而不是 undefined 关键字导致上述代码中的误报吗?
  • @kiran:实际上它正在使用 Alt+Tab。当您执行 Alt+Tab 时,您无法确定页面是否隐藏,因为您可能会切换到较小的窗口,因此您无法保证您的页面完全隐藏。这就是我使用“潜在隐藏”概念的原因(在示例中,阈值设置为 4 秒,因此您需要使用 Alt+Tab 切换到另一个窗口至少 4 秒)。但是您的评论表明答案不是很清楚,所以我重新措辞。
  • @JulienKronegg 我认为这是迄今为止最好的解决方案。但是,上面的代码非常需要一些重构和抽象。为什么不上传到 GitHub 让社区重构呢?
  • @Jacob 很高兴您喜欢我的解决方案。随意将其推广到自己的 GitHub 项目中。我提供带有知识共享许可的代码 BY creativecommons.org/licenses/by/4.0
  • @Caleb 不,我说的是网页前面的另一个应用程序(例如计算器)。在这种情况下,网页失去焦点,但仍然能够接收一些事件(例如鼠标悬停事件)。
【解决方案4】:

使用: Page Visibility API

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

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

【讨论】:

  • 问题不在于页面可见性。这是关于不活跃/活跃
  • 我觉得OP不是在说ide的功能
  • 我也不是在谈论 ide。我说的是 alt-tabbing/cmd-tabbing 到另一个应用程序。突然页面不活动了。页面可见性 api 不能帮助我知道页面是否处于非活动状态,它只能帮助我知道页面是否可能不可见。
【解决方案5】:

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 为我)

【讨论】:

  • 接受的答案导致 IE9 出现问题。这个库很好用。
  • 这个库完全被废弃了。虽然它看起来有一个打字稿版本,但它不再在 VSCode 中工作,甚至复制/粘贴源代码也有很多不再被认为是打字稿的好习惯
【解决方案6】:

我开始使用社区 wiki 答案,但意识到它没有检测 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) {
        visible = !document.hidden;
        document.addEventListener('visibilitychange',
            function() {(document.hidden ? unfocused : focused)()});
    }
    if ('mozHidden' in document) {
        visible = !document.mozHidden;
        document.addEventListener('mozvisibilitychange',
            function() {(document.mozHidden ? unfocused : focused)()});
    }
    if ('webkitHidden' in document) {
        visible = !document.webkitHidden;
        document.addEventListener('webkitvisibilitychange',
            function() {(document.webkitHidden ? unfocused : focused)()});
    }
    if ('msHidden' in document) {
        visible = !document.msHidden;
        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');
});

此版本侦听所有不同的可见性事件,并在其中任何一个导致更改时触发回调。 focusedunfocused 处理程序确保在多个 API 捕获相同的可见性更改时不会多次调用回调。

【讨论】:

  • Chrome 例如同时拥有document.hiddendocument.webkitHidden。如果没有if 构造中的else,我们会得到2 个回调调用,对吗?
  • @ChristiaanWesterbeek 说得好,我没想到!如果您可以编辑此帖子,请继续,我会接受:)
  • 呃,等一下:ChristiaanWesterbeek 建议添加“else”的编辑实际上是由@1.21Gigawatts 添加的,这似乎不是一个好主意:它破坏了最初购买 Daniel 的想法,即并行尝试所有支持的方法。并且没有回调被调用两次的风险,因为focused() 和unfocused() 在没有任何变化时会抑制额外的调用。看起来我们真的应该回到第一个版本。
  • 从今天开始检查,至少在 Chrome 78 + macos 上没有检测到 alt+tab
  • @HugoGresse 这个 sn-p 在 Chrome + MacOS 上运行良好。
【解决方案7】:

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

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

【讨论】:

  • 最干净的解决方案,支持回 IE6
  • document.hasFocus() 是最干净的方法。使用可见性 api 或基于事件的所有其他方法或寻找不同级别的用户活动/缺乏活动的所有其他方式都变得过于复杂,并且充满了边缘情况和漏洞。把它放在一个简单的间隔上,并在结果发生变化时引发自定义事件。示例:jsfiddle.net/59utucz6/1
  • 高效,与其他解决方案不同,当您切换到另一个浏览器选项卡或窗口,甚至是不同的应用程序时,它会提供正确的反馈。
  • 毫无疑问,这是最干净的方式,但在 Firefox 中不起作用
  • 如果我打开 Chrome 开发工具然后 document.hasFocus() 等于 false。或者即使您单击浏览器的顶部面板,也会发生同样的情况。我不确定这个解决方案是否适合暂停视频、动画等
【解决方案8】:

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

  • 该页面包含您无法控制的 iframe
  • 您希望跟踪可见性状态更改,而不管更改是由 TAB 更改 (ctrl+tab) 还是窗口更改 (alt+tab) 触发的

发生这种情况是因为:

  • 页面可见性 API 可以可靠地告诉您选项卡更改(即使使用 iframe),但它无法告诉您用户何时更改窗口。
  • 只要 iframe 没有焦点,侦听窗口模糊/焦点事件就可以检测到 alt+tab 和 ctrl+tab。

鉴于这些限制,有可能实施一个解决方案,将 - 页面可见性 API - 窗口模糊/焦点 - 文档.activeElement

能够:

  • 1) 父页面有焦点时ctrl+tab:YES
  • 2) iframe 获得焦点时 ctrl+tab:是
  • 3) 父页面有焦点时alt+tab:是
  • 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

我还没有用足够多的浏览器对此进行测试。 如果您能找到更多关于这不起作用的信息,请在下面的 cmets 中告诉我。

【讨论】:

  • 在我的测试中,它也适用于 Android 上的 IE9、IE10 和 Chrome。
  • 看来 IPAD 需要一个完全不同的解决方案 - stackoverflow.com/questions/4940657/…
  • 所有这些链接都是 404 :(
【解决方案9】:
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/

【讨论】:

    【解决方案10】:

    这适用于我在 chrome 67、firefox 67 上,

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

    【讨论】:

      【解决方案11】:

      这对我有用

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

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

      【讨论】:

        【解决方案12】:

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

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

        见:

        【讨论】:

        • 我认为这与BFCache有关:当用户单击后退或前进时 - 它与页面位于计算机桌面顶部无关。
        【解决方案13】:

        你可以使用:

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

        【讨论】:

          【解决方案14】:

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

          这有一个额外的好处,就是告诉用户是否空闲,而不是只是检查窗口是否处于非活动状态。

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

          【讨论】:

          • 除非用户没有鼠标。
          • @Annan:现在是codinghorror.com/blog/2007/03/…
          • 如果用户正在观看视频,这也不会掷骰子
          • 您可以使用 onkeypress 或其他类似事件来重置计时器并解决非鼠标问题。当然,对于积极查看页面以观看视频、研究图像等的用户来说,它仍然不起作用。
          【解决方案15】:

          这是对 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);
          }
          

          【讨论】:

          • 依靠焦点/模糊方法不起作用(它会给你很多误报),请参阅stackoverflow.com/a/9502074/698168
          【解决方案16】:

          对于没有 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

          【讨论】:

          • 浏览器支持怎么样?目前在 chrome、safari 和 firefox 中分叉很好。
          【解决方案17】:

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

          • 更改选项卡时
          • 更改窗口时(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...")
            }
          }
          

          【讨论】:

            【解决方案18】:

            对于 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 范围内的范围函数。

            【讨论】:

              【解决方案19】:

              如果您想整个浏览器模糊进行操作: 正如我评论的那样,如果浏览器失去焦点,则不会触发任何建议的事件。我的想法是循环计数并在事件触发时重置计数器。如果计数器达到限制,我会对另一个页面执行 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();
              

              这是在FF上测试成功的草稿。

              【讨论】:

                【解决方案20】:

                Chromium 团队目前正在开发Idle Detection API。它以origin trial since Chrome 88 的形式提供,这已经是此功能的第二个原始试用版。早期的原始试验是从 Chrome 84 到 Chrome 86。

                也可以通过标志启用:

                通过 chrome://flags 启用

                在本地试验 空闲检测 API,无需 原始试用令牌,启用 #enable-experimental-web-platform-features 标志在 chrome://flags.

                可以在此处找到演示:

                https://idle-detection.glitch.me/

                需要注意的是,此 API 基于权限(应该如此,否则可能会被滥用来监控用户的行为!)。

                【讨论】:

                  【解决方案21】:

                  这是一个可靠的现代解决方案。 (短个甜??)

                  document.addEventListener("visibilitychange", () => {
                    console.log( document.hasFocus() )
                  })
                  

                  这将设置一个侦听器,以便在触发任何可见性事件(可能是焦点或模糊)时触发。

                  【讨论】:

                  • 不适用于 Alt-Tab(切换到另一个应用程序)。
                  • alt + tab 在这里起作用了... (Chrome 91)
                  【解决方案22】:

                  我重读了@daniel-buckmaster 版本 我没有进行多次尝试,但是,代码对我来说似乎更优雅......

                  // on-visibility-change.js v1.0.1, based on https://stackoverflow.com/questions/1060008/is-there-a-way-to-detect-if-a-browser-window-is-not-currently-active#38710376
                  function onVisibilityChange(callback) {
                      let d = document;
                      let visible = true;
                      let prefix;
                      if ('hidden' in d) {
                          prefix = 'h';
                      } else if ('webkitHidden' in d) {
                          prefix = 'webkitH';
                      } else if ('mozHidden' in d) {
                          prefix = 'mozH';
                      } else if ('msHidden' in d) {
                          prefix = 'msH';
                      } else if ('onfocusin' in d) { // ie 9 and lower
                          d.onfocusin = focused;
                          d.onfocusout = unfocused;
                      } else { // others
                          window.onpageshow = window.onfocus = focused;
                          window.onpagehide = window.onblur = unfocused;
                      };
                      if (prefix) {
                          visible = !d[prefix + 'idden'];
                          d.addEventListener(prefix.substring(0, prefix.length - 1) + 'visibilitychange', function() {
                              (d[prefix + 'idden'] ? unfocused : focused)();
                          });
                      };
                  
                      function focused() {
                          if (!visible) {
                              callback(visible = true);
                          };
                      };
                  
                      function unfocused() {
                          if (visible) {
                              callback(visible = false);
                          };
                      };
                  };
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多