Hay 3 métodos típicos utilizados para determinar si el usuario puede ver la página HTML, sin embargo, ninguno de ellos funciona perfectamente:
Se supone que la API de visibilidad de página W3C debe hacer esto (compatible desde: Firefox 10, MSIE 10, Chrome 13). Sin embargo, esta API solo genera eventos cuando la pestaña del navegador se anula por completo (por ejemplo, cuando el usuario cambia de una pestaña a otra). La API no genera eventos cuando la visibilidad no se puede determinar con una precisión del 100% (por ejemplo, Alt + Tab para cambiar a otra aplicación).
El uso de métodos basados en enfoque / desenfoque le da muchos falsos positivos. Por ejemplo, si el usuario muestra una ventana más pequeña en la parte superior de la ventana del navegador, la ventana del navegador perderá el foco ( onblur
elevado) pero el usuario aún puede verlo (por lo que aún debe actualizarse). Ver también http://javascript.info/tutorial/focus
- Confiar en la actividad del usuario (movimiento del mouse, clics, tecla tecleada) también le da muchos falsos positivos. Piense en el mismo caso que el anterior, o en un usuario que mira un video.
Para mejorar los comportamientos imperfectos descritos anteriormente, utilizo una combinación de los 3 métodos: API de visibilidad W3C, luego enfoque / desenfoque y métodos de actividad del usuario para reducir la tasa de falsos positivos. Esto permite gestionar los siguientes eventos:
- Cambiar la pestaña del navegador a otra (100% de precisión, gracias a la API de Visibilidad de Página W3C)
- Página potencialmente oculta por otra ventana, por ejemplo, debido a Alt + Tab (probabilística = no 100% precisa)
- La atención del usuario no está centrada en la página HTML (probabilística = no es 100% precisa)
Así es como funciona: cuando el documento pierde el foco, la actividad del usuario (como el movimiento del mouse) en el documento se monitorea para determinar si la ventana es visible o no. La probabilidad de visibilidad de la página es inversamente proporcional al tiempo de la última actividad del usuario en la página: si el usuario no realiza actividad en el documento durante mucho tiempo, la página probablemente no sea visible. El siguiente código imita la API de visibilidad de página del W3C: se comporta de la misma manera pero tiene una pequeña tasa de falsos positivos. Tiene la ventaja de ser multi-navegador (probado en Firefox 5, Firefox 10, MSIE 9, MSIE 7, Safari 5, Chrome 9).
<div id = "x"> </div>
<script>
/ **
Registra el controlador para el evento para el objeto dado.
@param obj el objeto que generará el evento
@param evType el tipo de evento: click, keypress, mouseover, ...
@param fn la función de controlador de eventos
@param isCapturing establece el modo de evento (verdadero = evento de captura, falso = evento burbujeante)
@return true si el controlador de eventos se ha adjuntado correctamente
* /
función addEvent (obj, evType, fn, isCapturing) {
if (isCapturing == null) isCapturing = false;
if (obj.addEventListener) {
// Firefox
obj.addEventListener (evType, fn, isCapturing);
volver verdadero;
} else if (obj.attachEvent) {
// MSIE
var r = obj.attachEvent ('on' + evType, fn);
volver r;
} más {
falso retorno;
}
}
// registrarse en el cambio potencial de visibilidad de la página
addEvent (documento, "cambio potencial de visibilidad", función (evento) {
document.getElementById ("x"). innerHTML + = "possibleVisilityChange: potencialHidden =" + document.potentialHidden + ", document.potentiallyHiddenSince =" + document.potentiallyHiddenSince + "s <br>";
});
// regístrate en la API de visibilidad de página del W3C
var oculto = nulo;
var visibilidadCambio = nulo;
if (typeof document.mozHidden! == "undefined") {
hidden = "mozHidden";
visibilidadCambio = "mozvisibilitychange";
} else if (typeof document.msHidden! == "undefined") {
hidden = "msHidden";
visibilidadCambio = "msvisibilitychange";
} else if (typeof document.webkitHidden! == "undefined") {
hidden = "webkitHidden";
visibilidadCambio = "webkitvisibilitychange";
} else if (typeof document.hidden! == "hidden") {
hidden = "hidden";
visibilidadCambio = "Cambio de visibilidad";
}
if (oculto! = nulo &&Cambio! = nulo) {
addEvent (documento, visibilidadCambio, función (evento) {
document.getElementById ("x"). innerHTML + = visibilidadCambio + ":" + hidden + "=" + document [hidden] + "<br>";
});
}
var potencialPageVisibility = {
pageVisibilityChangeThreshold: 3 * 3600, // en segundos
init: function () {
función setAsNotHidden () {
var dispatchEventRequired = document.potentialHidden;
document.potentialHidden = false;
document.potentiallyHiddenSince = 0;
if (dispatchEventRequired) dispatchPageVisibilityChangeEvent ();
}
function initPotentiallyHiddenDetection () {
if (! hasFocusLocal) {
// la ventana no tiene el foco => verifica la actividad del usuario en la ventana
lastActionDate = new Date ();
if (timeoutHandler! = null) {
clearTimeout (timeoutHandler);
}
timeoutHandler = setTimeout (checkPageVisibility, potencialPageVisibility.pageVisibilityChangeThreshold * 1000 + 100); // +100 ms para evitar problemas de redondeo en Firefox
}
}
función dispatchPageVisibilityChangeEvent () {
unifiedVisilityChangeEventDispatchAllowed = false;
var evt = document.createEvent ("Evento");
evt.initEvent ("cambio potencial de visibilidad", verdadero, verdadero);
document.dispatchEvent (evt);
}
función checkPageVisibility () {
var potencialHiddenDuration = (hasFocusLocal || lastActionDate == null? 0: Math.floor ((new Date (). getTime () - lastActionDate.getTime ()) / 1000));
document.potentiallyHiddenSince = potencialHiddenDuration;
if (possibleHiddenDuration> = possiblePageVisibility.pageVisibilityChangeThreshold &&! document.potentialHidden) {
// umbral de cambio de visibilidad de página incrementado => elevar el par
document.potentialHidden = true;
dispatchPageVisibilityChangeEvent ();
}
}
var lastActionDate = nulo;
var hasFocusLocal = true;
var hasMouseOver = true;
document.potentialHidden = false;
document.potentiallyHiddenSince = 0;
var timeoutHandler = nulo;
addEvent (documento, "show de páginas", función (evento) {
document.getElementById ("x"). innerHTML + = "pageshow / doc: <br>";
});
addEvent (documento, "ocultación de página", función (evento) {
document.getElementById ("x"). innerHTML + = "pagehide / doc: <br>";
});
addEvent (ventana, "pagehow", función (evento) {
document.getElementById ("x"). innerHTML + = "pageshow / win: <br>"; // aparece cuando la página muestra por primera vez
});
addEvent (ventana, "ocultación de página", función (evento) {
document.getElementById ("x"). innerHTML + = "pagehide / win: <br>"; // no elevado
});
addEvent (documento, "mousemove", función (evento) {
lastActionDate = new Date ();
});
addEvent (documento, "mouseover", función (evento) {
hasMouseOver = verdadero;
setAsNotHidden ();
});
addEvent (documento, "mouseout", función (evento) {
hasMouseOver = falso;
initPotentiallyHiddenDetection ();
});
addEvent (ventana, "desenfoque", función (evento) {
hasFocusLocal = falso;
initPotentiallyHiddenDetection ();
});
addEvent (ventana, "foco", función (evento) {
hasFocusLocal = verdadero;
setAsNotHidden ();
});
setAsNotHidden ();
}
}
PotencialPageVisibility.pageVisibilityChangeThreshold = 4; // 4 segundos para probar
PotencialPageVisibility.init ();
</script>
Como actualmente no existe una solución de navegador cruzado que funcione sin falsos positivos, es mejor que lo piense dos veces antes de deshabilitar la actividad periódica en su sitio web.
requestAnimationFrame
API o use la función moderna de que la frecuencia desetTimeout
/setInterval
se reduce cuando la ventana no es visible (1 segundo en Chrome, por ejemplo).