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


149

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

情况是,我们有一个应用程序定期轮询股票价格,如果页面没有重点关注,我们可以停止轮询并为每个人节省交通噪音,尤其是当人们热衷于打开具有不同投资组合的多个标签时。

window.onblur并且window.onfocus为此选择吗?


Answers:


127

是的,window.onfocus并且window.onblur应该适合您的方案:

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


3
这样的onfocusin / onfocusout方面以及有关告诉用户您已暂停的注释都是非常好的注释。谢谢。
Fenton

7
请注意,您无法通过这种方式在页面加载时区分页面是活动页面还是非活动页面。
pimvdb 2011年

@SteveFenton - onfocus是跨浏览器,在这里你提到的事件是IE-而已,我不明白为什么这会被认为是一个很好的说明,你..
VSYNC

1
@vsync-阅读链接的文章,您将看到它同时使用 “ onfocusin”和“ onfocus”。
芬顿

您能否至少提及两者之间的区别?
Lenar Hoyt

53

重要编辑:这个答案已经过时。自编写以来,就引入了Visibility 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


2
只需注意一点,使用所有这些解决方案,您就冒着用户在javascript完全加载之前更改选项卡的风险,从而将错误的值分配给了focus。不确定周围是否有好的方法。
JayD3e 2013年

更新链接正是我想要的。感谢您添加它们!
webLacky3rdClass

问题特别是关于检测页面是否具有焦点,这不同于检测页面是否可见。可以同时(在不同的窗口中)看到多个页面,而只有一个页面可以具有焦点。使用适合您需要的任何技术,但要知道区别。
jaredjacobs 18/09/19

1
这是一个危险的解决方案,因为它具有在较大的应用程序中覆盖某些其他事件侦听器的风险。您应该改用以下答案:stackoverflow.com/a/21935031/549503
mmmeff

51

在搜索此问题时,我发现应该使用Page Visibility API的建议。大多数现代浏览器根据“我可以使用”来支持此API:http : //caniuse.com/#feat=pagevisibility

这是一个工作示例(来自此片段):

$(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特定的前缀。


当供应商前缀从这里转到时,我可能会切换!
Fenton

唯一真正的问题不是供应商前缀,因为存在正式的W3C建议(日期为2013年10月29日)。在某些情况下,问题在于IE10和更高版本支持页面可见性API。如果您需要支持IE9,则应该寻找其他方法…
Ilija 2014年

这是所有现代浏览器的正确方法。+1
Ajedi32

您确定甚至需要这些供应商前缀吗?根据MDN和CanIUse的说法,自32版以来在Chrome上就不需要它们,而自17版以来在Firefox上就不再需要它们,而在IE上就不再需要它们。
Ajedi32

@ Ajedi32谢谢。我需要进行一些测试和挖掘,以了解它仍然相关,以及现在可以忽略的内容。
伊利亚,2015年

37

惊讶地看到没人提及 document.hasFocus

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

MDN有更多信息。


为我工作(在Chrome和Firefox上测试)。接受的答案(的onfocus /的onblur)并没有工作
harmv

正确答案再次在最底部。去StackOverflow的路!
10月11

真的,这不是完美的答案吗?有人看到不利之处吗?
加斯帕

2
唯一的缺点是,如果您要尝试从iframe中确定选项卡是否处于焦点位置,那么如果在父页面仍然没有焦点的情况下加载了iframe,则会失败。为了涵盖这一点,您还必须使用页面可见性api。
伊万

29

是的,这些应该为您工作。您只是让我想起了我遇到的利用这些技术的链接。有趣的读物


2
+1-这是一个非常聪明的把戏,我可以想象这欺骗了很多人。
Fenton

2
多么巧妙和vious回的攻击。有趣的读到,谢谢。
Voo

4

我会这样做(参考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给出的答案有何不同吗?虽然可能有所不同,但这是微妙的。因此,请解释一下它的含义以及为什么应该有所不同。
Fenton

2

跨浏览器jQuery解决方案! 原始可从GitHub获得

有趣且易于使用!

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

  • onblur | .blur / onfocus | .focus“ 重复 ”调用
  • 通过选择替代应用程序(例如word),窗口失去了焦点
    • 这往往是不受欢迎的,因为,如果您打开了银行页面,并且onblur事件告诉它屏蔽了该页面,那么如果您打开计算器,您将无法再看到该页面!
  • 没有在页面加载时触发

使用非常简单:向下滚动到“ 运行代码段

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


您应该将插件的代码最小化。
Patrick Desjardins 2015年

@PatrickDesjardins是的。计划在这个周末与其他事情一起做。一世?为我拥有的一堆东西要点。GitHub上的Jdmckinstry。当我将其添加到
摘要时,

当我切换到另一个应用程序(如“ Word”或“ Calculator”)时,如果希望页面失去焦点怎么办?
贝纳斯

@Benas可能是错误的,但我确实认为这是非常基本的基本功能,jQuery(window).blur/focus许多人都不希望这样做,因此,我制作此插件的原因之一。该插件旨在帮助提供jQuery尚未提供的功能
SpYk3HH 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.