Detectar si la pestaña del navegador tiene foco


149

¿Existe una forma confiable de navegador cruzado para detectar que una pestaña tiene foco?

El escenario es que tenemos una aplicación que sondea regularmente los precios de las acciones, y si la página no tiene foco, podríamos detener el sondeo y ahorrar a todos el ruido del tráfico, especialmente porque las personas son fanáticas de abrir varias pestañas con diferentes carteras.

¿Es window.onblury window.onfocusuna opción para esto?


Respuestas:


127

Sí, window.onfocusy window.onblurdebería funcionar para su escenario:

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


3
El aspecto onfocusin / onfocusout de esto, y también la nota sobre decirle al usuario que ha hecho una pausa son notas realmente buenas. Gracias.
Fenton

77
Tenga en cuenta que no puede distinguir entre la página activa o inactiva al cargar la página de esta manera.
pimvdb

@SteveFenton - onfocuses crossbrowser, donde los eventos que has mencionado son solo para IE, no puedo ver por qué esto sería considerado una buena nota para ti ..
vsync

1
@vsync - leer el artículo enlazado, verá que utiliza tanto 'onfocusin' y 'onfocus'.
Fenton

¿Podría al menos mencionar la diferencia entre los dos?
Lenar Hoyt

53

Edición importante: esta respuesta está desactualizada. Desde que lo escribió, se introdujo la API de visibilidad ( mdn , ejemplo , especificación ). Es la mejor manera de resolver este problema.


var focused = true;

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

AFAIK, focusy blurtodos son compatibles con ... todo. (ver http://www.quirksmode.org/dom/events/index.html )


2
Solo una pequeña nota, con todas estas soluciones, corre el riesgo de que el usuario cambie las pestañas antes de que el javascript esté completamente cargado, asignando así el valor incorrecto a enfocado. No estoy seguro de que haya una buena forma de evitarlo.
JayD3e

Los enlaces de actualización son exactamente lo que estaba buscando. ¡Gracias por agregarlos!
webLacky3rdClass

La pregunta es específicamente acerca de detectar si una página tiene foco, lo cual es diferente de detectar si la página es visible. Se pueden ver varias páginas al mismo tiempo (en diferentes ventanas), mientras que solo una puede tener el foco. Use la técnica que se adapte a sus necesidades, pero sepa la diferencia.
jaredjacobs

1
Esta es una solución peligrosa porque corre el riesgo de anular algún otro detector de eventos en una aplicación más grande. En su lugar, debe seguir esta respuesta: stackoverflow.com/a/21935031/549503
mmmeff

51

Mientras buscaba este problema, encontré una recomendación de que se debería usar la API de Visibilidad de página . La mayoría de los navegadores modernos son compatibles con esta API de acuerdo con Can I Use: http://caniuse.com/#feat=pagevisibility .

Aquí hay un ejemplo de trabajo (derivado de este fragmento ):

$(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];
    }
  });
});

Actualización: el ejemplo anterior solía tener propiedades prefijadas para los navegadores Gecko y WebKit, pero eliminé esa implementación porque estos navegadores han estado ofreciendo API de Visibilidad de Página sin un prefijo por un tiempo. Mantuve el prefijo específico de Microsoft para seguir siendo compatible con IE10.


Cuando los prefijos del proveedor pasen de esto, ¡probablemente cambiaré!
Fenton

El único problema real con esto no son los prefijos de los proveedores porque hay una recomendación oficial del W3C (fechada el 29 de octubre de 2013). Lo que es un problema en algunos casos es que la API de visibilidad de página es compatible con IE10 y posteriores. Si necesita admitir IE9, debe buscar un enfoque diferente ...
Ilija

Esta es la forma correcta de hacerlo para todos los navegadores modernos. +1
Ajedi32

¿Estás seguro de que estos prefijos de proveedor son necesarios? Según MDN y CanIUse, no han sido necesarios en Chrome desde la versión 32, o en Firefox desde la versión 17, y nunca han sido necesarios en IE.
Ajedi32

@ Ajedi32 Gracias. Tendré que hacer algunas pruebas y cavar para ver qué sigue siendo relevante y qué se puede omitir ahora.
Ilija

37

Sorprendente ver a nadie mencionado document.hasFocus

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

MDN tiene más información.


funciona para mí (probado en Chrome y Firefox). La respuesta aceptada (onfocus / onblur) no funcionó
harmv el

La respuesta correcta una vez más en la parte inferior. Así se hace StackOverflow!
Once de octubre

realmente, ¿no es esta la respuesta perfecta? ¿Alguien ve alguna desventaja?
gaspar

2
El único inconveniente de esto es que si está tratando de determinar si la pestaña está enfocada dentro de un iframe, entonces fallaría en caso de que el iframe se cargara cuando la página principal aún no estuviera enfocada. Para cubrir eso también tendrás que ir con la API de visibilidad de la página.
Ivan

29

Sí, eso debería funcionar para ti. Me acabas de recordar este enlace que encontré que explota esas técnicas. lecturA INTERESANTE


2
+1: es un truco muy inteligente, me imagino que engaña a mucha gente.
Fenton

2
Qué ataque tan ingenioso y tortuoso. Interesante leer eso, gracias.
Voo

4

Lo haría de esta manera (Referencia 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
        }

    };  

¿Puede explicar cómo esta respuesta difiere de la respuesta dada por @Ilija? Puede haber una diferencia, pero es sutil, por lo que se agradecería una explicación de lo que es y por qué debería ser diferente.
Fenton

2

Cross Browser jQuery Solution! Raw disponible en GitHub

¡Diversión y fácil de usar!

El siguiente complemento pasará por su prueba estándar para varias versiones de IE, Chrome, Firefox, Safari, etc. y establecerá sus métodos declarados en consecuencia. También trata temas como:

  • onblur | .blur / onfocus | .focus llamadas " duplicadas "
  • ventana que pierde el foco mediante la selección de una aplicación alternativa, como word
    • Esto tiende a ser indeseable simplemente porque, si tiene una página de banco abierta, y su evento onblur le dice que enmascare la página, entonces si abre la calculadora, ¡ya no podrá ver la página!
  • No se activa al cargar la página

El uso es tan simple como: Desplácese hacia abajo para ' Ejecutar fragmento '

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


Debe poner el código sin minimizar para el complemento.
Patrick Desjardins

@PatrickDesjardins, sí. Planee hacer esto este fin de semana junto con otras cosas. ¿YO? Haz una idea general de un montón de cosas que tengo. Jdmckinstry en github. Agregaré enlaces a respuestas antiguas como estas a medida que las agregue a
gist

¿Qué sucede si deseo que la página pierda el foco cuando cambio a otra aplicación, como "Word" o "Calculadora"?
Benas

@Benas Podría estar equivocado, pero creo que esa es la funcionalidad básica de lo muy básico jQuery(window).blur/focus, que muchos no querían, por lo que es una de las razones por las que hice este complemento. El complemento está destinado a ayudar a proporcionar lo que jQuery ya no tiene
SpYk3HH
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.