Il existe 3 méthodes typiques utilisées pour déterminer si l'utilisateur peut voir la page HTML, mais aucune ne fonctionne parfaitement:
L' API de visibilité de page du W3C est censée faire cela (prise en charge depuis: Firefox 10, MSIE 10, Chrome 13). Cependant, cette API ne déclenche des événements que lorsque l'onglet du navigateur est entièrement remplacé (par exemple lorsque l'utilisateur passe d'un onglet à un autre). L'API ne déclenche pas d'événements lorsque la visibilité ne peut pas être déterminée avec une précision de 100% (par exemple Alt + Tab pour passer à une autre application).
L'utilisation de méthodes basées sur le focus / flou vous donne beaucoup de faux positifs. Par exemple, si l'utilisateur affiche une fenêtre plus petite en haut de la fenêtre du navigateur, la fenêtre du navigateur perdra le focus ( onblur
surélevée) mais l'utilisateur est toujours en mesure de la voir (il doit donc être actualisé). Voir aussi http://javascript.info/tutorial/focus
- S'appuyer sur l'activité de l'utilisateur (déplacement de la souris, clics, frappe au clavier) vous donne également beaucoup de faux positifs. Pensez au même cas que ci-dessus, ou à un utilisateur qui regarde une vidéo.
Afin d'améliorer les comportements imparfaits décrits ci-dessus, j'utilise une combinaison des 3 méthodes: API de visibilité W3C, puis focus / flou et méthodes d'activité utilisateur afin de réduire le taux de faux positifs. Cela permet de gérer les événements suivants:
- Changer l'onglet du navigateur en un autre (précision de 100%, grâce à l'API W3C Page Visibility)
- Page potentiellement masquée par une autre fenêtre, par exemple en raison de Alt + Tab (probabiliste = pas 100% précis)
- L'attention des utilisateurs n'est peut-être pas concentrée sur la page HTML (probabiliste = pas précis à 100%)
Voici comment cela fonctionne: lorsque le document perd le focus, l'activité de l'utilisateur (comme le déplacement de la souris) sur le document est surveillée afin de déterminer si la fenêtre est visible ou non. La probabilité de visibilité de la page est inversement proportionnelle à l'heure de la dernière activité de l'utilisateur sur la page: si l'utilisateur ne fait aucune activité sur le document pendant une longue période, la page n'est probablement pas visible. Le code ci-dessous imite l'API W3C Page Visibility: il se comporte de la même manière mais a un petit taux de faux positifs. Il a l'avantage d'être multibrowser (testé sur Firefox 5, Firefox 10, MSIE 9, MSIE 7, Safari 5, Chrome 9).
<div id = "x"> </div>
<script>
/ **
Enregistre le gestionnaire de l'événement pour l'objet donné.
@param obj l'objet qui déclenchera l'événement
@param evType le type d'événement: clic, pression de touche, survol de la souris, ...
@param fn la fonction du gestionnaire d'événements
@param isCapturing définit le mode d'événement (true = événement de capture, false = événement de propagation)
@return true si le gestionnaire d'événements a été correctement attaché
* /
fonction addEvent (obj, evType, fn, isCapturing) {
if (isCapturing == null) isCapturing = false;
if (obj.addEventListener) {
// Firefox
obj.addEventListener (evType, fn, isCapturing);
return true;
} else if (obj.attachEvent) {
// MSIE
var r = obj.attachEvent ('on' + evType, fn);
return r;
} autre {
retour faux;
}
}
// s'inscrire au changement potentiel de visibilité de page
addEvent (document, "potentialvisilitychange", fonction (événement) {
document.getElementById ("x"). innerHTML + = "potentialVisilityChange: potentialHidden =" + document.potentialHidden + ", document.potentialHiddenSince =" + document.potentialHiddenSince + "s <br>";
});
// s'inscrire à l'API de visibilité de page du W3C
var caché = null;
var visibilitéChange = null;
if (typeof document.mozHidden! == "undefined") {
hidden = "mozHidden";
visibilitéChange = "mozvisibilitychange";
} else if (typeof document.msHidden! == "undefined") {
hidden = "msHidden";
visibilitéChange = "msvisibilitychange";
} else if (typeof document.webkitHidden! == "undefined") {
hidden = "webkitHidden";
visibilitéChange = "webkitvisibilitychange";
} else if (typeof document.hidden! == "hidden") {
caché = "caché";
visibilitéChange = "visibilitéchange";
}
if (caché! = null && visibilitéChange! = null) {
addEvent (document, visibilitéChange, fonction (événement) {
document.getElementById ("x"). innerHTML + = visibilitéChange + ":" + caché + "=" + document [caché] + "<br>";
});
}
var potentialPageVisibility = {
pageVisibilityChangeThreshold: 3 * 3600, // en secondes
init: fonction () {
fonction setAsNotHidden () {
var dispatchEventRequired = document.potentialHidden;
document.potentialHidden = false;
document.potentialHiddenSince = 0;
if (dispatchEventRequired) dispatchPageVisibilityChangeEvent ();
}
fonction initPotentialHiddenDetection () {
if (! hasFocusLocal) {
// la fenêtre n'a pas le focus => vérifier l'activité des utilisateurs dans la fenêtre
lastActionDate = new Date ();
if (timeoutHandler! = null) {
clearTimeout (timeoutHandler);
}
timeoutHandler = setTimeout (checkPageVisibility, potentialPageVisibility.pageVisibilityChangeThreshold * 1000 + 100); // +100 ms pour éviter les problèmes d'arrondi sous Firefox
}
}
function dispatchPageVisibilityChangeEvent () {
unifiedVisilityChangeEventDispatchAllowed = false;
var evt = document.createEvent ("Event");
evt.initEvent ("potentialvisilitychange", vrai, vrai);
document.dispatchEvent (evt);
}
function checkPageVisibility () {
var potentialHiddenDuration = (hasFocusLocal || lastActionDate == null? 0: Math.floor ((new Date (). getTime () - lastActionDate.getTime ()) / 1000));
document.potentialHiddenSince = potentialHiddenDuration;
if (potentialHiddenDuration> = potentialPageVisibility.pageVisibilityChangeThreshold &&! document.potentialHidden) {
// seuil de changement de visibilité de la page raiched => augmenter le pair
document.potentialHidden = true;
dispatchPageVisibilityChangeEvent ();
}
}
var lastActionDate = null;
var hasFocusLocal = true;
var hasMouseOver = true;
document.potentialHidden = false;
document.potentialHiddenSince = 0;
var timeoutHandler = null;
addEvent (document, "pageshow", fonction (événement) {
document.getElementById ("x"). innerHTML + = "pageshow / doc: <br>";
});
addEvent (document, "pagehide", fonction (événement) {
document.getElementById ("x"). innerHTML + = "pagehide / doc: <br>";
});
addEvent (fenêtre, "pageshow", fonction (événement) {
document.getElementById ("x"). innerHTML + = "pageshow / win: <br>"; // levé lorsque la page s'affiche pour la première fois
});
addEvent (fenêtre, "pagehide", fonction (événement) {
document.getElementById ("x"). innerHTML + = "pagehide / win: <br>"; // non levé
});
addEvent (document, "mousemove", fonction (événement) {
lastActionDate = new Date ();
});
addEvent (document, "survol de la souris", fonction (événement) {
hasMouseOver = true;
setAsNotHidden ();
});
addEvent (document, "mouseout", fonction (événement) {
hasMouseOver = false;
initPotiallyHiddenDetection ();
});
addEvent (fenêtre, "flou", fonction (événement) {
hasFocusLocal = false;
initPotiallyHiddenDetection ();
});
addEvent (fenêtre, "focus", fonction (événement) {
hasFocusLocal = true;
setAsNotHidden ();
});
setAsNotHidden ();
}
}
potentialPageVisibility.pageVisibilityChangeThreshold = 4; // 4 secondes pour les tests
potentialPageVisibility.init ();
</script>
Puisqu'il n'y a actuellement aucune solution multi-navigateur qui fonctionne sans faux positif, vous devriez mieux réfléchir à deux fois avant de désactiver l'activité périodique sur votre site Web.
requestAnimationFrame
API ou utilisez la fonctionnalité moderne selon laquelle la fréquence desetTimeout
/setInterval
est réduite lorsque la fenêtre n'est pas visible (1 seconde dans Chrome, par exemple).