API Page Visibility : le document HTML est-il visible par l'utilisateur ?
Article par dew (Alsacréations, Strasbourg)
Lorsqu'il s'agit d'aborder les performances des navigateurs web, de nombreux sujets existent : optimisation JavaScript, diminution des ressources à télécharger, meilleure écriture des sélecteurs CSS. Une autre fonctionnalité existe pour soulager le processeur : savoir si un document HTML est affiché ou non. Dans ce dernier cas, il sera possible de mettre en pause les traitements qui sont invisibles à l'utilisateurs et ne sont dès lors plus utiles, par exemple :
- mettre en pause une vidéo ou l'audio
- mettre en pause une animation Canvas, WebGL ou SVG
- ne pas effectuer de requêtes de type AJAX en arrière-plan pour rafraîchir les données
- ne pas afficher de notifications
L'API Page Visibility renseigne sur la visibilité d'une page, qu'elle occupe une fenêtre à elle toute seule ou juste un onglet. Dès qu'il y a changement d'état, un événement JavaScript est déclenché.
Notez que tout ceci n'a absolument rien à voir avec les propriétés CSS display et visibility puisqu'il s'agit d'un état global du document et non d'un élément HTML en particulier.
La théorie
Cette fonctionnalité est construite autour de l'événement visibilitychange, qui va être déclenché par le navigateur lorsqu'il considérera que son contenu n'est pas « visible » par l'utilisateur :
- Si la fenêtre est réduite (minimisée)
- Si l'onglet n'est pas actif (l'utilisateur consulte une autre page web dans un autre onglet si le navigateur le permet)
Deux propriétés de l'interface document seront disponibles pour surveiller son état.
La première est document.hidden qui est un booléen :
-
true(caché) -
false(affiché)

La deuxième est document.visibilityState qui correspond à plusieurs états :
-
hidden: pas du tout visible, -
visible: affiché totalement ou en partie, -
prerender: chargement hors-écran non visible, -
unloaded: en cours d'être déchargé de la mémoire

L'état prerender correspond au pré-chargement du document par le navigateur et à son pré-calcul (rendu graphique). C'est une technique de plus en plus répandue et inaugurée par Chrome pour anticiper la navigation de l'utilisateur. En mode prerender, le document va être chargé de manière invisible en arrière-plan, le code JavaScript sera exécuté de manière classique. Quel est l'intérêt de faire cette distinction ? On pourra différer le chargement des outils de statistiques visiteurs, puisqu'il ne s'agira pas d'une consultation réelle mais d'une anticipation par le navigateur. Ceux-ci pourront reprendre leur exécution normale lorsqu'on passera en mode visible.
Tableau des compatibilités
| Navigateurs | Versions |
|---|---|
|
|
Internet Explorer 10 avec préfixe -ms- |
|
|
Firefox 10 avec préfixe |
|
|
Chrome 14 avec préfixe -webkit-Chrome Mobile 18 avec préfixe -webkit- |
|
|
Opera 12.1 Opera Mobile 12.1 |
|
|
Safari pas encore début 2013 |
![]() |
Android Browser pas encore au début 2013 |
![]() |
BlackBerry 10 avec préfixe -webkit- |
La pratique
Pour mettre en place une surveillance de la visibilité du document, il faudra associer l'événement visibilitychange à une fonction (ci-après nommée changementVisibilite) qui va traiter le résultat.
Détection du changement de visibilité
document.addEventListener('visibilitychange', changementVisibilite, false);
function changementVisibilite() {
// On examine les propriétés :
// document.hidden
// document.visibilityState
// ...
}
Cet exemple minimal (et idéal) ne tient pas compte de la réalité des préfixes navigateurs, qui dans les premières versions de prise en charge, sont dotés de -moz- (Mozilla Firefox), -ms- (Microsoft Internet Explorer), -webkit- (Chrome/Safari/autres). L'exemple ci-après est plus complet.
Détection du changement de visibilité avec préfixes navigateurs
<html>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Visibilité ?</title>
<p id="log"></p>
</head>
<body>
<script>
// Détection préalable des préfixes pour chaque moteur
// et stockage de leur nom dans des variables
var hidden, 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";
}
// La variable hidden contient le nom de la propriété du document
// La variable visibilityChange contient le nom de l'événement
// La variable visibilityState contient la propriété d'état
document.addEventListener(visibilityChange, changementVisibilite, false);
// Elément qui va permettre un affichage informatif
var log = document.getElementById('log');
// Fonction qui traite l'événement
function changementVisibilite() {
log.innerHTML += 'Le document est caché ? '+document[hidden]+'<br>';
log.innerHTML += 'Etat : '+document[visibilityState]+'<br>';
}
</script>
</body>
</html>
Oui, cela fait tout de suite beaucoup plus de code, c'est contraignant et confus. Si vous souhaitez expérimenter la chose sur toutes les plate-formes par anticipation, il n'y a pas possibilité d'y échapper avant un abandon généralisé des préfixes pour cette fonctionnalité.
Le passage d'un onglet à l'autre, ou la réduction de la fenêtre provoque un aller-retour entre document caché et document visible (état hidden puis visible).
Dans la démonstration suivante, ce petit test est mené pour afficher des messages texte relatifs à la visibilité. Le reste du script reste le même.

Dans la dernière démonstration, il s'agit de modifier le comportement d'une animation Canvas : lorsque celle-ci détecte l'invisibilité, elle peut ralentir (ou se mettre en pause) et changer son processus. Pour cet exemple, l'avancement change de couleur (en vert lorsque le document est affiché, en rouge sinon, avec un défilement plus lent).

Globalement, le test reste toujours le même : se renseigner sur l'état et prendre des mesures en conséquence. Par exemple pour arrêter la lecture d'un élément <video id="mavideo"> :
if(document[hidden]) {
document.getElementById('mavideo').pause();
} else {
document.getElementById('mavideo').play();
}
Il ne reste plus qu'à appliquer ce principe à toutes les actions qui peuvent en profiter (voir les exemples de cas pratiques mentionnés en introduction).





Muchos a dit le
Super API ! Merci pour l'article et les démos.