Existem três métodos típicos usados para determinar se o usuário pode ver a página HTML, mas nenhum deles funciona perfeitamente:
A API do W3C Page Visibility deve fazer isso (suportada desde: Firefox 10, MSIE 10, Chrome 13). No entanto, essa API somente gera eventos quando a guia do navegador é totalmente substituída (por exemplo, quando o usuário muda de uma guia para outra). A API não gera eventos quando a visibilidade não pode ser determinada com 100% de precisão (por exemplo, Alt + Tab para alternar para outro aplicativo).
O uso de métodos baseados em foco / desfoque fornece muitos falsos positivos. Por exemplo, se o usuário exibir uma janela menor na parte superior da janela do navegador, ela perderá o foco ( onblur
aumentado), mas o usuário ainda poderá vê-lo (portanto, ainda precisará ser atualizado). Veja também http://javascript.info/tutorial/focus
- Confiar na atividade do usuário (movimento do mouse, cliques, tecla digitada) também fornece muitos falsos positivos. Pense no mesmo caso acima, ou em um usuário assistindo a um vídeo.
Para melhorar os comportamentos imperfeitos descritos acima, uso uma combinação dos 3 métodos: API de visibilidade do W3C, em seguida, foco / desfoque e métodos de atividade do usuário para reduzir a taxa de falsos positivos. Isso permite gerenciar os seguintes eventos:
- Alterando a guia do navegador para outra (100% de precisão, graças à API de visibilidade da página do W3C)
- Página potencialmente oculta por outra janela, por exemplo, devido a Alt + Tab (probabilística = não 100% precisa)
- Atenção do usuário potencialmente não focada na página HTML (probabilística = não 100% precisa)
É assim que funciona: quando o documento perde o foco, a atividade do usuário (como a movimentação do mouse) no documento é monitorada para determinar se a janela está visível ou não. A probabilidade de visibilidade da página é inversamente proporcional à hora da última atividade do usuário na página: se o usuário não faz atividade no documento por um longo tempo, é provável que a página não esteja visível. O código abaixo imita a API de visibilidade da página W3C: ela se comporta da mesma maneira, mas possui uma pequena taxa de falsos positivos. Tem a vantagem de ser multibrowser (testado no Firefox 5, Firefox 10, MSIE 9, MSIE 7, Safari 5, Chrome 9).
<div id = "x"> </div>
<script>
/ **
Registra o manipulador no evento para o objeto especificado.
@param obj o objeto que irá gerar o evento
@param evType o tipo de evento: clique, tecla pressionada, mouseover, ...
@param fn a função de manipulador de eventos
@param isCapturing define o modo de evento (true = evento de captura, false = evento de bolhas)
@return true se o manipulador de eventos foi anexado corretamente
* /
função addEvent (obj, evType, fn, isCapturing) {
if (isCapturing == null) isCapturing = false;
if (obj.addEventListener) {
// Raposa de fogo
obj.addEventListener (evType, fn, isCapturing);
return true;
} se if (obj.attachEvent) {
// MSIE
var r = obj.attachEvent ('on' + evType, fn);
retornar r;
} outro {
retorna falso;
}
}
// registre-se na possível alteração na visibilidade da página
addEvent (documento, "mudança de visibilidade potencial", função (evento) {
document.getElementById ("x"). innerHTML + = "potencialVisilityChange: potencialHidden =" + document.potentialHidden + ", document.potentiallyHiddenSince =" + document.potentiallyHiddenSince + "s <br>";
});
// registre-se na API de visibilidade da página W3C
var oculto = nulo;
var visibleChange = null;
if (typeof document.mozHidden! == "undefined") {
oculto = "mozHidden";
visibleChange = "mozvisibilitychange";
} else if (typeof document.msHidden! == "undefined") {
oculto = "msHidden";
visibleChange = "msvisibilitychange";
} else if (typeof document.webkitHidden! == "undefined") {
hidden = "webkitHidden";
visibleChange = "webkitvisibilitychange";
} else if (typeof document.hidden! == "hidden") {
oculto = "oculto";
visibleChange = "visiblechange";
}
if (oculto! = nulo && visibleChange! = nulo) {
addEvent (documento, visibleChange, função (evento) {
document.getElementById ("x"). innerHTML + = visibleChange + ":" + hidden + "=" + documento [oculto] + "<br>";
});
}
var potencialPageVisibility = {
pageVisibilityChangeThreshold: 3 * 3600, // em segundos
init: function () {
função setAsNotHidden () {
var dispatchEventRequired = document.potentialHidden;
document.potentialHidden = false;
document.potentiallyHiddenSince = 0;
if (dispatchEventRequired) dispatchPageVisibilityChangeEvent ();
}
função initPotentiallyHiddenDetection () {
if (! hasFocusLocal) {
// a janela não tem o foco => verifica a atividade do usuário na janela
lastActionDate = new Date ();
if (timeoutHandler! = null) {
clearTimeout (timeoutHandler);
}
timeoutHandler = setTimeout (checkPageVisibility, potencialPageVisibility.pageVisibilityChangeThreshold * 1000 + 100); // +100 ms para evitar problemas de arredondamento no Firefox
}
}
função dispatchPageVisibilityChangeEvent () {
unifiedVisilityChangeEventDispatchAllowed = false;
var evt = document.createEvent ("Evento");
evt.initEvent ("mudança de visibilidade potencial", verdadeira, verdadeira);
document.dispatchEvent (evt);
}
função checkPageVisibility () {
var potencialHiddenDuration = (hasFocusLocal || lastActionDate == null? 0: Math.floor ((new Date (). getTime () - lastActionDate.getTime ()) / 1000));
document.potentiallyHiddenSince = potencialHiddenDuration;
if (potencialHiddenDuration> = potencialPageVisibility.pageVisibilityChangeThreshold &&! document.potentialHidden) {
// limiar de alteração da visibilidade da página raiched => aumenta o nível
document.potentialHidden = true;
dispatchPageVisibilityChangeEvent ();
}
}
var lastActionDate = nulo;
var hasFocusLocal = true;
var hasMouseOver = true;
document.potentialHidden = false;
document.potentiallyHiddenSince = 0;
var timeoutHandler = nulo;
addEvent (documento, "pageshow", função (evento) {
document.getElementById ("x"). innerHTML + = "pageshow / doc: <br>";
});
addEvent (documento, "pagehide", função (evento) {
document.getElementById ("x"). innerHTML + = "ocultar página / doc: <br>";
});
addEvent (janela, "pageshow", função (evento) {
document.getElementById ("x"). innerHTML + = "pageshow / win: <br>"; // gerado quando a página é exibida pela primeira vez
});
addEvent (janela, "ocultar página", função (evento) {
document.getElementById ("x"). innerHTML + = "pagehide / win: <br>"; // não gerado
});
addEvent (documento, "mousemove", função (evento) {
lastActionDate = new Date ();
});
addEvent (documento, "mouseover", função (evento) {
hasMouseOver = true;
setAsNotHidden ();
});
addEvent (documento, "mouseout", função (evento) {
hasMouseOver = false;
initPotentiallyHiddenDetection ();
});
addEvent (janela, "desfoque", função (evento) {
hasFocusLocal = false;
initPotentiallyHiddenDetection ();
});
addEvent (janela, "foco", função (evento) {
hasFocusLocal = true;
setAsNotHidden ();
});
setAsNotHidden ();
}
}
potencialPageVisibility.pageVisibilityChangeThreshold = 4; // 4 segundos para teste
potencialPageVisibility.init ();
</script>
Como atualmente não existe uma solução que funcione em vários navegadores sem falso positivo, é melhor pensar duas vezes em desativar a atividade periódica em seu site.
requestAnimationFrame
API ou use o recurso moderno de que a frequência desetTimeout
/setInterval
é reduzida quando a janela não está visível (1 segundo no Chrome, por exemplo).