Detectar se a guia do navegador tem foco

149

Existe uma maneira confiável entre navegadores para detectar que uma guia está em foco.

O cenário é que temos um aplicativo que pesquisa regularmente os preços das ações e, se a página não tiver foco, poderíamos interromper a pesquisa e poupar a todos o ruído do tráfego, principalmente porque as pessoas são fãs de abrir várias guias com portfólios diferentes.

É window.onblure window.onfocusuma opção para isso?

Fenton
fonte

Respostas:

127

Sim window.onfocuse window.onblurdeve funcionar para o seu cenário:

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

Ryan Wright
fonte
3
O aspecto onfocusin / onfocusout disso, e também a nota sobre dizer ao usuário que você pausou, são realmente boas notas. Obrigado.
Fenton
7
Observe que você não pode distinguir entre a página ativa ou inativa no carregamento da página dessa maneira.
Pimvdb
@SteveFenton - onfocusé crossbrowser, onde os eventos que você mencionou são IE-only, eu não posso ver porque isso seria considerado uma boa nota por você ..
vsync
1
@vsync - leia o artigo ligado, você verá que ele usa tanto 'onfocusin' e 'onfocus'.
Fenton
Você poderia ao menos mencionar a diferença entre os dois?
Lenar Hoyt
53

Edição importante: esta resposta está desatualizada. Desde sua criação, a API Visibility ( mdn , exemplo , spec ) foi introduzida. É a melhor maneira de resolver esse problema.


var focused = true;

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

AFAIK, focuse blurtodos são suportados em ... tudo. (consulte http://www.quirksmode.org/dom/events/index.html )

Zirak
fonte
2
Apenas uma pequena nota, com todas essas soluções, você corre o risco de o usuário alterar as guias antes que o javascript seja totalmente carregado, atribuindo assim o valor errado ao foco. Não tenho certeza se existe uma boa maneira de contornar isso.
JayD3e
Os links de atualização são exatamente o que eu estava procurando. Obrigado por adicioná-los!
WebLacky3rdClass
A pergunta é especificamente sobre como detectar se uma página tem foco, o que é diferente de detectar se a página está visível. Várias páginas podem ser visíveis ao mesmo tempo (em janelas diferentes), enquanto apenas uma pode ter foco. Use qualquer técnica que atenda às suas necessidades, mas saiba a diferença.
Jaredjacobs 19/09/19
1
Essa é uma solução perigosa porque corre o risco de substituir outro ouvinte de evento em um aplicativo maior. Em vez disso você deve seguir esta resposta: stackoverflow.com/a/21935031/549503
mmmeff
51

Ao pesquisar sobre esse problema, encontrei uma recomendação de que a API de visibilidade de página deveria ser usada. A maioria dos navegadores modernos suporta essa API de acordo com Can I Use: http://caniuse.com/#feat=pagevisibility .

Aqui está um exemplo de trabalho (derivado deste 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];
    }
  });
});

Atualização: o exemplo acima costumava ter propriedades com prefixo para os navegadores Gecko e WebKit, mas eu removi essa implementação porque esses navegadores oferecem a API de visibilidade de página sem um prefixo há algum tempo. Mantive o prefixo específico da Microsoft para permanecer compatível com o IE10.

Ilija
fonte
Quando os prefixos do fornecedor passarem disso, provavelmente mudarei!
Fenton
O único problema real com isso não são os prefixos do fornecedor, porque há uma recomendação oficial do W3C (29 de outubro de 2013). O problema em alguns casos é que a API de visibilidade da página é suportada no IE10 e mais recente. Se você precisa dar suporte ao IE9, deve procurar uma abordagem diferente ...
Ilija
Essa é a maneira correta de fazer isso para todos os navegadores modernos. 1
Ajedi32
Você tem certeza de que esses prefixos de fornecedores são necessários? De acordo com o MDN e o CanIUse, eles não são necessários no Chrome desde a versão 32 ou no Firefox desde a versão 17 e nunca no IE.
precisa saber é o seguinte
@ Ajedi32 Obrigado. Vou precisar fazer alguns testes e escavações para ver o que ainda é relevante e o que pode ser deixado de fora agora.
Ilija
37

Surpreendente ver ninguém mencionado document.hasFocus

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

MDN tem mais informações.

aleclarson
fonte
funciona para mim (testado no Chrome e Firefox). A resposta aceita (onfocus / onblur) se não trabalhar
harmv
A resposta correta mais uma vez na parte inferior. Caminho a percorrer StackOverflow!
Onze de outubro
realmente, não é a resposta perfeita? alguém vê alguma desvantagem?
gaspar
2
A única desvantagem disso é que, se você estiver tentando determinar se a guia está em foco em um iframe, ela falhará caso o iframe seja carregado quando a página pai ainda estiver fora de foco. Para cobrir isso, você também precisará usar a API de visibilidade da página.
Ivan
29

Sim, esses devem funcionar para você. Você acabou de me lembrar desse link que me deparei que explora essas técnicas. leitura interessante

Brian Glaz
fonte
2
+1 - esse é um truque muito inteligente, eu poderia imaginar isso enganando muita gente.
Fenton
2
Que ataque engenhoso e desonesto. Interessante ler isso, obrigado.
Voo 12/09
4

Eu faria dessa maneira (Referência 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
        }

    };  
confundir
fonte
Você pode explicar como essa resposta difere da resposta dada por @Ilija - pode haver uma diferença, mas é sutil -, então uma explicação sobre o que é e por que deveria ser diferente seria apreciada.
Fenton
2

Solução jQuery para vários navegadores! Raw disponível no GitHub

Divertido e fácil de usar!

O plug-in a seguir fará seu teste padrão para várias versões do IE, Chrome, Firefox, Safari, etc. e estabelecerá os métodos declarados de acordo. Também lida com questões como:

  • onblur | .blur / onfocus | .focus chamadas " duplicadas "
  • janela perdendo o foco através da seleção de aplicativo alternativo, como palavra
    • Isso tende a ser indesejável, simplesmente porque, se você tiver uma página bancária aberta, e o evento onblur solicitar para mascarar a página, se você abrir a calculadora, não poderá mais ver a página!
  • Não é acionado no carregamento da página

O uso é tão simples quanto: Role para baixo até ' Executar 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
fonte
Você deve colocar o código não minimizado para o plugin.
Patrick Desjardins
@PatrickDesjardins yeah. Planeje fazer isso neste fim de semana junto com outras coisas. EU? Faça uma essência para um monte de coisas que eu tenho. Jdmckinstry no github. Irá adicionar links para velhas respostas como estes como eu levá-los adicionado a essência
SpYk3HH
E se eu quiser que a página perca o foco quando mudar para outro aplicativo, como "Word" ou "Calculator"?
Benas
@ Benas Pode estar errado, mas acredito que essa é a funcionalidade básica do básico jQuery(window).blur/focus, que era indesejável por muitos, portanto, uma das razões pelas quais fiz esse plugin. O plug-in foi criado para ajudar a fornecer o que o jQuery ainda não
possui #