检测浏览器选项卡是不是有焦点

Posted

技术标签:

【中文标题】检测浏览器选项卡是不是有焦点【英文标题】:Detect If Browser Tab Has Focus检测浏览器选项卡是否有焦点 【发布时间】:2011-11-15 09:39:41 【问题描述】:

是否有可靠的跨浏览器方法来检测选项卡是否具有焦点。

场景是我们有一个定期轮询股票价格的应用程序,如果页面没有焦点,我们可以停止轮询并为每个人节省交通噪音,特别是当人们喜欢打开几个不同的标签时投资组合。

window.onblurwindow.onfocus 可以选择吗?

【问题讨论】:

是的,这些应该对你有用。你刚刚让我想起了我遇到的这个利用这些技术的链接。 interesting read Is there a way to detect if a browser window is not currently active?的可能重复 【参考方案1】:

是的,window.onfocuswindow.onblur 应该适用于您的场景:

http://www.thefutureoftheweb.com/blog/detect-browser-window-focus

【讨论】:

这方面的 onfocusin/onfocusout 方面,以及关于告诉用户您已暂停的注释都是非常好的注释。谢谢。 请注意,您无法通过这种方式在页面加载时区分页面处于活动状态还是非活动状态。 @SteveFenton - onfocus 是跨浏览器,你提到的事件仅限于 IE,我不明白为什么这会被你认为是个好笔记.. @vsync - 阅读链接的文章,您会发现它同时使用了both“onfocusin”和“onfocus”。 您能否至少提一下两者之间的区别?【参考方案2】:

惊讶地发现没有人提到document.hasFocus

if (document.hasFocus()) console.log('Tab is active')

MDN has more information.

【讨论】:

对我有用(在 Chrome 和 Firefox 上测试)。接受的答案(onfocus/onblur)没有工作 正确答案再次出现在最底部。 *** 好样的! 真的,这不是完美的答案吗?有人觉得有什么缺点吗? 唯一的缺点是,如果您尝试从 iframe 中确定选项卡是否处于焦点位置,那么如果 iframe 在父页面仍然不在时加载,它将失败重点。要涵盖这一点,您还必须使用页面可见性 api。 如果你只是点击选项卡标题,document.hasFocus() 值不会改变,因为它在文档上,你必须点击文档才能看到变化。这是一个缺点。【参考方案3】:

重要编辑:此答案已过时。自编写以来,引入了可见性 API(mdn、example、spec)。这是解决这个问题的更好方法。


var focused = true;

window.onfocus = function() 
    focused = true;
;
window.onblur = function() 
    focused = false;
;

AFAIK、focusblur 都支持...一切。 (见http://www.quirksmode.org/dom/events/index.html

【讨论】:

请注意,使用所有这些解决方案,您冒着用户在 javascript 完全加载之前更改选项卡的风险,从而为焦点分配了错误的值。不知道有没有好的方法。 更新链接正是我想要的。感谢您添加它们! 问题具体是检测一个页面是否有焦点,这和检测页面是否可见是不同的。可以同时看到多个页面(在不同的窗口中),而只有一个页面可以具有焦点。使用适合您需要的任何技术,但要知道区别。 这是一个危险的解决方案,因为它冒着在更大的应用程序中覆盖其他事件侦听器的风险。你应该遵循这个答案:***.com/a/21935031/549503【参考方案4】:

在搜索这个问题时,我发现应该使用Page Visibility API 的建议。根据 Can I Use:http://caniuse.com/#feat=pagevisibility,大多数现代浏览器都支持此 API。

这是一个工作示例(源自this snippet):

$(document).ready(function() 
  var hidden, visibilityState, visibilityChange;

  if (typeof document.hidden !== "undefined") 
    hidden = "hidden", visibilityChange = "visibilitychange", visibilityState = "visibilityState";
   else if (typeof document.msHidden !== "undefined") 
    hidden = "msHidden", visibilityChange = "msvisibilitychange", visibilityState = "msVisibilityState";
  

  var document_hidden = document[hidden];

  document.addEventListener(visibilityChange, function() 
    if(document_hidden != document[hidden]) 
      if(document[hidden]) 
        // Document hidden
       else 
        // Document shown
      

      document_hidden = document[hidden];
    
  );
);

更新: 上面的示例曾经为 Gecko 和 WebKit 浏览器提供前缀属性,但我删除了该实现,因为这些浏览器已经提供不带前缀的 Page Visibility API 一段时间了。为了与 IE10 保持兼容,我保留了 Microsoft 特定的前缀。

【讨论】:

当供应商前缀从此消失时,我可能会切换! 唯一真正的问题不是供应商前缀,因为有官方 W3C 建议(日期为 2013 年 10 月 29 日)。在某些情况下,问题是 IE10 和更高版本支持页面可见性 API。如果你需要支持 IE9,你应该寻找不同的方法…… 这是所有现代浏览器的正确方法。 +1 你确定这些供应商前缀是必要的吗?根据 MDN 和 CanIUse 的说法,Chrome 从 32 版开始就不需要它们,Firefox 从 17 版开始就不需要它们,而且 IE 也从未需要它们。 @Ajedi32 谢谢。我需要做一些测试和挖掘,看看它仍然相关的内容,以及现在可以省略的内容。【参考方案5】:

我会这样做(参考http://www.w3.org/TR/page-visibility/):

    window.onload = function() 

        // check the visiblility of the page
        var hidden, visibilityState, visibilityChange;

        if (typeof document.hidden !== "undefined") 
            hidden = "hidden", visibilityChange = "visibilitychange", visibilityState = "visibilityState";
        
        else if (typeof document.mozHidden !== "undefined") 
            hidden = "mozHidden", visibilityChange = "mozvisibilitychange", visibilityState = "mozVisibilityState";
        
        else if (typeof document.msHidden !== "undefined") 
            hidden = "msHidden", visibilityChange = "msvisibilitychange", visibilityState = "msVisibilityState";
        
        else if (typeof document.webkitHidden !== "undefined") 
            hidden = "webkitHidden", visibilityChange = "webkitvisibilitychange", visibilityState = "webkitVisibilityState";
        


        if (typeof document.addEventListener === "undefined" || typeof hidden === "undefined") 
            // not supported
        
        else 
            document.addEventListener(visibilityChange, function() 
                console.log("hidden: " + document[hidden]);
                console.log(document[visibilityState]);

                switch (document[visibilityState]) 
                case "visible":
                    // visible
                    break;
                case "hidden":
                    // hidden
                    break;
                
            , false);
        

        if (document[visibilityState] === "visible") 
            // visible
        

    ;  

【讨论】:

您能解释一下这个答案与@Ilija 给出的答案有何不同 - 可能存在差异,但它很微妙 - 所以解释它是什么以及为什么应该不同将不胜感激. 感谢@confile!该代码是不言自明且易于使用的。将此实现为一个函数,它就像一个魅力 - 跨浏览器和跨平台,包括移动浏览器!【参考方案6】:

发布此答案是因为我在接受的答案中发现了一个错误。

错误是当您在焦点窗口上打开开发者控制台并单击它的任意位置时,开发者控制台现在具有焦点,此时 window.onfocuswindow.onblur 没有任何作用。

所以这是我的解决方案,

document.addEventListener("visibilitychange", function() 
    if (document.visibilityState === 'visible') 
        console.log('has focus');
     else 
        console.log('lost focus');
    
);

阅读更多https://developer.mozilla.org/en-US/docs/Web/API/Document/visibilitychange_event

【讨论】:

【参考方案7】:

跨浏览器 jQuery 解决方案! Raw 可在GitHub获得

有趣且易于使用!

以下插件将通过您对各种版本的 IE、Chrome、Firefox、Safari 等的标准测试,并相应地建立您声明的方法。它还处理以下问题:

onblur|.blur/onfocus|.focus "重复" 调用 通过选择备用应用程序(如 word)导致窗口失去焦点 这往往是不可取的,因为如果您打开了一个银行页面,并且它的 onblur 事件告诉它屏蔽该页面,那么如果您打开计算器,您将看不到该页面没有了! 页面加载时不触发

使用很简单:向下滚动到'Run Snippet'

$.winFocus(function(event, isVisible) 
    console.log("Combo\t\t", event, isVisible);
);

//  OR Pass False boolean, and it will not trigger on load,
//  Instead, it will first trigger on first blur of current tab_window
$.winFocus(function(event, isVisible) 
    console.log("Combo\t\t", event, isVisible);
, false);

//  OR Establish an object having methods "blur" & "focus", and/or "blurFocus"
//  (yes, you can set all 3, tho blurFocus is the only one with an 'isVisible' param)
$.winFocus(
    blur: function(event) 
        console.log("Blur\t\t", event);
    ,
    focus: function(event) 
        console.log("Focus\t\t", event);
    
);

//  OR First method becoms a "blur", second method becoms "focus"!
$.winFocus(function(event) 
    console.log("Blur\t\t", event);
,
function(event) 
    console.log("Focus\t\t", event);
);

/*    Begin Plugin    */
;;(function($)$.winFocus||($.extend(winFocus:function()var a=!0,b=[];$(document).data("winFocus")||$(document).data("winFocus",$.winFocus.init());for(x in arguments)"object"==typeof arguments[x]?(arguments[x].blur&&$.winFocus.methods.blur.push(arguments[x].blur),arguments[x].focus&&$.winFocus.methods.focus.push(arguments[x].focus),arguments[x].blurFocus&&$.winFocus.methods.blurFocus.push(arguments[x].blurFocus),arguments[x].initRun&&(a=arguments[x].initRun)):"function"==typeof arguments[x]?b.push(arguments[x]):
"boolean"==typeof arguments[x]&&(a=arguments[x]);b&&(1==b.length?$.winFocus.methods.blurFocus.push(b[0]):($.winFocus.methods.blur.push(b[0]),$.winFocus.methods.focus.push(b[1])));if(a)$.winFocus.methods.onChange()),$.winFocus.init=function()$.winFocus.props.hidden in document?document.addEventListener("visibilitychange",$.winFocus.methods.onChange):($.winFocus.props.hidden="mozHidden")in document?document.addEventListener("mozvisibilitychange",$.winFocus.methods.onChange):($.winFocus.props.hidden=
"webkitHidden")in document?document.addEventListener("webkitvisibilitychange",$.winFocus.methods.onChange):($.winFocus.props.hidden="msHidden")in document?document.addEventListener("msvisibilitychange",$.winFocus.methods.onChange):($.winFocus.props.hidden="onfocusin")in document?document.onfocusin=document.onfocusout=$.winFocus.methods.onChange:window.onpageshow=window.onpagehide=window.onfocus=window.onblur=$.winFocus.methods.onChange;return $.winFocus,$.winFocus.methods=blurFocus:[],blur:[],focus:[],
exeCB:function(a)$.winFocus.methods.blurFocus&&$.each($.winFocus.methods.blurFocus,function(b,c)this.apply($.winFocus,[a,!a.hidden]));a.hidden&&$.winFocus.methods.blur&&$.each($.winFocus.methods.blur,function(b,c)this.apply($.winFocus,[a]));!a.hidden&&$.winFocus.methods.focus&&$.each($.winFocus.methods.focus,function(b,c)this.apply($.winFocus,[a])),onChange:function(a)var b=focus:!1,focusin:!1,pageshow:!1,blur:!0,focusout:!0,pagehide:!0;if(a=a||window.event)a.hidden=a.type in b?b[a.type]:
document[$.winFocus.props.hidden],$(window).data("visible",!a.hidden),$.winFocus.methods.exeCB(a);else try$.winFocus.methods.onChange.call(document,new Event("visibilitychange"))catch(c),$.winFocus.props=hidden:"hidden"))(jQuery);
/*    End Plugin      */

// Simple example
$(function() 
	$.winFocus(function(event, isVisible) 
		$('td tbody').empty();
		$.each(event, function(i) 
			$('td tbody').append(
				$('<tr />').append(
					$('<th />',  text: i ),
					$('<td />',  text: this.toString() )
				)
			)
		);
		if (isVisible) 
			$("#isVisible").stop().delay(100).fadeOut('fast', function(e) 
				$('body').addClass('visible');
				$(this).stop().text('TRUE').fadeIn('slow');
			);
		else 
			$('body').removeClass('visible');
			$("#isVisible").text('FALSE');
		
	);
)
body  background: #AAF; 
table  width: 100%; 
table table  border-collapse: collapse; margin: 0 auto; width: auto; 
tbody > tr > th  text-align: right; 
td  width: 50%; 
th, td  padding: .1em .5em; 
td th, td td  border: 1px solid; 
.visible  background: #FFA; 
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h3>See Console for Event Object Returned</h3>
<table>
    <tr>
        <th><p>Is Visible?</p></th>
        <td><p id="isVisible">TRUE</p></td>
    </tr>
    <tr>
        <td colspan="2">
            <table>
                <thead>
                    <tr>
                        <th colspan="2">Event Data <span style="font-size: .8em;"> See Console for More Details </span></th>
                    </tr>
                </thead>
                <tbody></tbody>
            </table>
        </td>
    </tr>
</table>

【讨论】:

您应该为插件取消最小化代码。 @PatrickDesjardins 是的。计划这个周末和其他事情一起做。一世?为我拥有的一堆东西做一个要点。 github 上的 Jdmckinstry。当我将它们添加到 gist 时,将添加指向此类旧答案的链接 如果我希望页面在切换到另一个应用程序(如“Word”或“Calculator”)时失去焦点怎么办? @Benas 可能是错的,但我确实相信这是非常基本的jQuery(window).blur/focus 的基本功能,这是许多人不想要的,这也是我制作这个插件的原因之一。该插件旨在帮助提供 jQuery 尚未提供的功能【参考方案8】:

这是 10 岁。这是一个较新的版本:Pause/resume CSS animations when switching tabs

基本使用Mozillas Javascript https://developer.mozilla.org/en-US/docs/Web/API/Window/focus_event

 function pause() 

 //Do something

 

 function play() 

 //Do something

 
 window.addEventListener('blur', pause);
 window.addEventListener('focus', play);

【讨论】:

以上是关于检测浏览器选项卡是不是有焦点的主要内容,如果未能解决你的问题,请参考以下文章

如何在javascript中检测浏览器选项卡是不是关闭或浏览器窗口

当浏览器选项卡没有焦点时使用 Websocket 进行角冻结

javascript 当用户失去对站点的焦点时,切换浏览器选项卡上的文本(标题)(切换选项卡)

辅助功能 - 如何在元素上设置自定义选项卡焦点顺序

检测两个浏览器窗口是不是对同一个站点打开

在 Mobile Safari 中通过 JavaScript 检测关闭浏览器选项卡?