ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

我的参考点:如果您使用的窗口不活动,Gmail Chat 会播放声音。

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

1
19 revs, 10 users 67%

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

document.addEventListener("visibilitychange", onchange);

当前浏览器支持:

铬 13+

互联网浏览器 10+

火狐 10+

Opera 12.10+ [阅读笔记]

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

(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 事件,而是从任务栏最大化另一个程序。这种情况有解决方案吗?谢谢!
C
Carson Wright

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

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

或者至少它对我有用。


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

有 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 上测试)。


    <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>

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


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

使用: 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 不能帮助我知道页面是否不活动,它只能帮助我知道页面是否可能不可见。
o
omnomnom

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

法郎 26.0

铬 34.0

...可能还有所有较新的版本。

不完全适用于:

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


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

我开始使用社区 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 这个片段在 Chrome + MacOS 上运行得非常好。
G
Grey Li

我为我的应用程序创建了一个 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。或者即使您单击浏览器的顶部面板,也会发生同样的情况。我不确定这个解决方案是否适合暂停视频、动画等
T
Tony Lâmpada

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

该页面包含您无法控制的 iframe

无论是由 TAB 更改 (ctrl+tab) 还是窗口更改 (alt+tab) 触发的更改,您都希望跟踪可见性状态更改

发生这种情况是因为:

页面可见性 API 可以可靠地告诉您选项卡更改(即使使用 iframe),但它不能告诉您用户何时更改窗口。

只要 iframe 没有焦点,侦听窗口模糊/焦点事件就可以检测到 alt+tabs 和 ctrl+tabs。

鉴于这些限制,有可能实现一个结合 - 页面可见性 API - 窗口模糊/焦点 - document.activeElement 的解决方案

这能够:

1) 父页面有焦点时 ctrl+tab: YES

2) iframe 有焦点时 ctrl+tab: YES

3)当父页面有焦点时alt+tab:是

4) 当 iframe 有焦点时 alt+tab:NO <-- 无赖

当 iframe 具有焦点时,您的模糊/焦点事件根本不会被调用,并且页面可见性 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

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


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


S
Samad

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

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

S
Sahil Ralkar

这对我有用

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

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


s
sim642

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

onpageshow:当窗口可见时运行的脚本

onpagehide:隐藏窗口时运行的脚本

看:

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

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


我认为这与 BFCache 有关:当用户单击后退或前进时 - 它与位于计算机桌面顶部的页面无关。
a
adrihanu

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

更改选项卡时

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

m
maryam

你可以使用:

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

})();

A
Austin Hyde

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

这有一个额外的好处是告诉用户是否空闲,而不是仅仅检查窗口是否不活动。

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


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

这是对 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
N
Niko

对于没有 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)的后备方案也可用


浏览器支持呢?目前在 chrome、safari 和 firefox 中分叉很好。
S
Steve Campbell

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


L
Luke H

如果您想对整个浏览器模糊采取行动:正如我所评论的,如果浏览器失去焦点,则不会触发任何建议的事件。我的想法是循环计数并在事件触发时重置计数器。如果计数器达到限制,我会对另一个页面执行 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上测试成功的草稿。


c
connexo

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

它也可以通过标志启用:

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

可以在此处找到演示:

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

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


L
Luis Lobo

我重读了@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);
        };
    };
};

C
Cory Robinson

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

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

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


不适用于 Alt-Tab(切换到另一个应用程序)。
这里 alt + tab 起作用了......(Chrome 91)
A
Akbarali

我的代码

let browser_active = ((typeof document.hasFocus != 'undefined' ? document.hasFocus() : 1) ? 1 : 0);
if (!browser_active) {
 // active
}