Detect If Browser Tab Has Focus

2019-01-01 13:02发布

问题:

Is there a reliable cross-browser way to detect that a tab has focus.

The scenario is that we have an application that polls regularly for stock prices, and if the page doesn\'t have focus we could stop the polling and save everyone the traffic noise, especially as people are fans of opening several tabs with different portfolios.

Is window.onblur and window.onfocus an option for this?

回答1:

Yes, window.onfocus and window.onblur should work for your scenario:

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



回答2:

Important Edit: This answer is outdated. Since writing it, the Visibility API (mdn, example, spec) has been introduced. It is the better way to solve this problem.


var focused = true;

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

AFAIK, focus and blur are all supported on...everything. (see http://www.quirksmode.org/dom/events/index.html )



回答3:

While searching about this problem, I found a recommendation that Page Visibility API should be used. Most modern browsers support this API according to Can I Use: http://caniuse.com/#feat=pagevisibility.

Here\'s a working example (derived from 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];
    }
  });
});

Update: The example above used to have prefixed properties for Gecko and WebKit browsers, but I removed that implementation because these browsers have been offering Page Visibility API without a prefix for a while now. I kept Microsoft specific prefix in order to stay compatible with IE10.



回答4:

Yes those should work for you. You just reminded me of this link I came across that exploits those techniques. interesting read



回答5:

Surprising to see nobody mentioned document.hasFocus

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

MDN has more information.



回答6:

I would do it this way (Reference 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
        }

    };  


回答7:

Cross Browser jQuery Solution! Raw available at GitHub

Fun & Easy to Use!

The following plugin will go through your standard test for various versions of IE, Chrome, Firefox, Safari, etc.. and establish your declared methods accordingly. It also deals with issues such as:

  • onblur|.blur/onfocus|.focus \"duplicate\" calls
  • window losing focus through selection of alternate app, like word
    • This tends to be undesirable simply because, if you have a bank page open, and it\'s onblur event tells it to mask the page, then if you open calculator, you can\'t see the page anymore!
  • Not triggering on page load

Use is as simple as: Scroll Down to \'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>