Определить, есть ли у вкладки браузера фокус

149

Существует ли надежный кросс-браузерный способ определения того, что вкладка имеет фокус.

Сценарий состоит в том, что у нас есть приложение, которое регулярно опрашивает цены акций, и если страница не имеет фокуса, мы могли бы прекратить опрос и спасти всех от шума дорожного движения, особенно потому, что люди являются поклонниками открытия нескольких вкладок с различными портфелями.

Есть window.onblurи window.onfocusвариант для этого?

Фентон
источник

Ответы:

127

Да, window.onfocusи window.onblurдолжно работать для вашего сценария:

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

Райан Райт
источник
3
Этот аспект onfocusin / onfocusout, а также примечание о том, как сказать пользователю, что вы сделали паузу, являются действительно хорошими заметками. Спасибо.
Фентон,
7
Обратите внимание, что таким образом вы не можете различить активную или неактивную страницу при загрузке страницы.
pimvdb
@SteveFenton - onfocusэто кроссбраузер, где упомянутые вами события предназначены только для IE, я не могу понять, почему это было бы для вас хорошим замечанием ..
vsync
1
@vsync - прочитайте связанную статью, вы увидите, что она использует и onfocusin, и onfocus.
Фентон
Не могли бы вы хотя бы упомянуть разницу между ними?
Ленар Хойт
53

Важное изменение: этот ответ устарел. С момента его написания был представлен API видимости ( mdn , example , spec ). Это лучший способ решить эту проблему.


var focused = true;

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

AFAIK, focusи blurвсе поддерживаются на ... все. (см. http://www.quirksmode.org/dom/events/index.html ).

Zirak
источник
2
Небольшое замечание: со всеми этими решениями вы рискуете, что пользователь поменяет вкладки до полной загрузки javascript, назначив неправильное значение для focus. Не уверен, что есть хороший способ обойти это.
JayD3e
Ссылки на обновления - это именно то, что я искал. Спасибо за добавление их!
webLacky3rdClass
Вопрос заключается, в частности, в том, чтобы определить, имеет ли страница фокус, что отличается от определения, является ли страница видимой. Несколько страниц могут быть видны одновременно (в разных окнах), в то время как только одна может иметь фокус. Используйте технику, которая соответствует вашим потребностям, но знайте разницу.
Джареджакобс
1
Это опасное решение, поскольку существует риск переопределения какого-либо другого прослушивателя событий в более крупном приложении. Вместо этого вы должны следовать этому ответу: stackoverflow.com/a/21935031/549503
mmmeff
51

При поиске этой проблемы я нашел рекомендацию использовать 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, но я удалил эту реализацию, потому что эти браузеры уже некоторое время предлагают API видимости страницы без префикса. Я сохранил специфический префикс Microsoft, чтобы оставаться совместимым с IE10.

Ilija
источник
Когда префиксы продавца идут от этого, я, вероятно, переключусь!
Фентон
Единственная реальная проблема в этом - не префиксы вендоров, потому что есть официальная рекомендация W3C (от 29 октября 2013 г.). В некоторых случаях проблема заключается в том, что API видимости страницы поддерживается в IE10 и новее. Если вам нужно поддерживать IE9, вы должны искать другой подход ...
Илья
Это правильный способ сделать это для всех современных браузеров. +1
Ajedi32
Вы уверены, что эти префиксы продавцов даже необходимы? Согласно MDN и CanIUse, они не были необходимы в Chrome начиная с версии 32 или в Firefox начиная с версии 17, и они никогда не были необходимы в IE.
Ajedi32
@ Ajedi32 Спасибо. Мне нужно сделать несколько тестов и покопаться, чтобы увидеть, что это все еще актуально, а что можно оставить сейчас.
Илья
37

Удивительно видеть, что никто не упомянул document.hasFocus

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

MDN имеет больше информации.

aleclarson
источник
у меня работает (проверено на Chrome и Firefox). Принятый ответ (onfocus / onblur) не сработал
Вред
Правильный ответ еще раз в самом низу. Путь к StackOverflow!
11 октября
действительно, разве это не идеальный ответ? Кто-нибудь видит какие-либо недостатки?
Гаспар
2
Единственным недостатком этого является то, что если вы пытаетесь определить, находится ли вкладка в фокусе изнутри iframe, то он потерпит неудачу, если iframe был загружен, когда родительская страница все еще была не в фокусе. Чтобы покрыть это, вы должны пойти с API видимости страницы.
Иван
29

Да, те должны работать на вас. Вы только что напомнили мне об этой ссылке, с которой я столкнулся, которая использует эти методы. интересно читать

Брайан Глаз
источник
2
+1 - это очень умный трюк, я мог представить, что обманывают много людей.
Фентон
2
Какая гениальная и коварная атака. Интересно почитать, спасибо.
Во
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
        }

    };  
confile
источник
Можете ли вы объяснить, чем этот ответ отличается от ответа, данного @Ilija, - разница может быть, но она неуловима - поэтому будет полезно получить пояснение, что это такое и почему он должен отличаться.
Фентон
2

Кросс-браузерное решение jQuery! Сырье доступно на GitHub

Веселье и простота в использовании!

Следующий плагин пройдет стандартный тест для различных версий IE, Chrome, Firefox, Safari и т. Д. И соответственно установит заявленные вами методы. Он также занимается такими вопросами, как:

  • onblur | .blur / onfocus | .focus " дублировать " вызовы
  • окно теряет фокус при выборе альтернативного приложения, например слова
    • Как правило, это нежелательно просто потому, что если у вас открыта банковская страница, и событие 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>

SpYk3HH
источник
Вы должны поместить код не минимизированный для плагина.
Патрик Дежарден
@PatrickDesjardins да. Планируйте делать это в эти выходные вместе с другими вещами. Я? Сделайте суть для множества вещей, которые у меня есть. Jdmckinstry в github. Будут добавлены ссылки на старые ответы, подобные этим, когда я
добавлю
Что, если я хочу, чтобы страница теряла фокус, когда я переключаюсь на другое приложение, такое как «Слово» или «Калькулятор»?
Бенас
@Benas Могу ошибаться, но я верю, что это базовая функциональность самого базового jQuery(window).blur/focus, что было нежелательным для многих, поэтому одна из причин, по которой я сделал этот плагин. Плагин предназначен для предоставления того, что jQuery еще не делает
SpYk3HH