OUJOOD.COM
Un utilisateur a souvent plusieurs onglets ouverts en même temps. Quand votre page est en arrière-plan, continuer à jouer des animations, envoyer des requêtes toutes les secondes ou faire tourner des calculs coûteux est inutile — et pénalise la batterie et les performances globales de l'appareil.
La Page Visibility API résout ce problème avec deux propriétés et un événement. C'est l'une des APIs les plus légères de HTML5 : quelques lignes suffisent pour rendre une application nettement plus efficace.
Les deux propriétés essentielles
document.hidden retourne un booléen simple. document.visibilityState donne plus de détails avec une chaîne de caractères :
Exemple : 📋 Copier le code
// Vérifier si la page est cachée console.log(document.hidden); // true si l'onglet est en arrière-plan, false s'il est visible // État détaillé de la visibilité console.log(document.visibilityState); // "visible" — l'onglet est actif et visible // "hidden" — l'onglet est en arrière-plan ou la fenêtre est minimisée
Détecter les changements de visibilité
L'événement visibilitychange se déclenche à chaque fois que l'utilisateur change d'onglet, minimise la fenêtre, ou revient sur la page. C'est le cœur de l'API :
Exemple : 📋 Copier le code
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
console.log('Onglet en arrière-plan — mise en pause.');
} else {
console.log('Onglet redevenu visible — reprise.');
}
});
Mettre en pause une vidéo ou une animation
Le cas d'usage le plus direct : mettre en pause automatiquement une vidéo quand l'utilisateur change d'onglet, et la reprendre à son retour :
Exemple : 📋 Copier le code
const video = document.getElementById('maVideo');
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
video.pause();
} else {
// Reprendre seulement si la vidéo était en lecture
video.play();
}
});
Suspendre un intervalle de polling
Beaucoup d'applications interrogent le serveur à intervalles réguliers pour récupérer des données fraîches. Continuer ce polling quand l'onglet est invisible gaspille la bande passante et les ressources serveur. La solution : suspendre l'intervalle et le relancer au retour :
Exemple : 📋 Copier le code
let intervalId = null;
function demarrerPolling() {
if (intervalId) return; // déjà actif
intervalId = setInterval(async () => {
const data = await fetch('/api/notifications').then(r => r.json());
mettreAJourInterface(data);
}, 5000);
}
function arreterPolling() {
clearInterval(intervalId);
intervalId = null;
}
// Démarrer ou arrêter selon la visibilité
document.addEventListener('visibilitychange', () => {
document.hidden ? arreterPolling() : demarrerPolling();
});
// Démarrer au chargement si l'onglet est déjà visible
if (!document.hidden) demarrerPolling();
Optimiser les animations avec requestAnimationFrame
Les animations basées sur requestAnimationFrame sont déjà ralenties automatiquement par le navigateur quand l'onglet est en arrière-plan, mais les arrêter complètement économise encore plus de ressources :
Exemple : 📋 Copier le code
let animationId = null;
function animer(timestamp) {
// Logique d'animation ici
dessinerFrame(timestamp);
animationId = requestAnimationFrame(animer);
}
function demarrerAnimation() {
if (!animationId) {
animationId = requestAnimationFrame(animer);
}
}
function arreterAnimation() {
if (animationId) {
cancelAnimationFrame(animationId);
animationId = null;
}
}
document.addEventListener('visibilitychange', () => {
document.hidden ? arreterAnimation() : demarrerAnimation();
});
demarrerAnimation();
Exemple pratique : chronomètre précis
Un chronomètre basé sur setInterval dérive quand l'onglet est en arrière-plan car les intervals sont throttlés par le navigateur. La solution correcte : mesurer le temps réel écoulé avec Date.now(), et reprendre proprement à la visibilité :
Exemple : 📋 Copier le code
let debutTemps = null;
let tempsEcoule = 0;
let intervalChrono = null;
function demarrerChrono() {
debutTemps = Date.now();
intervalChrono = setInterval(() => {
const maintenant = Date.now();
tempsEcoule += maintenant - debutTemps;
debutTemps = maintenant;
afficherTemps(tempsEcoule);
}, 100);
}
function arreterChrono() {
clearInterval(intervalChrono);
intervalChrono = null;
}
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
// Sauvegarder le temps au moment où l'onglet se cache
tempsEcoule += Date.now() - debutTemps;
arreterChrono();
} else {
// Reprendre depuis le temps sauvegardé
demarrerChrono();
}
});
Suivre le temps de présence réel
La Page Visibility API permet aussi de mesurer le temps qu'un utilisateur passe réellement sur une page — en ne comptant que les périodes où l'onglet est visible :
Exemple : 📋 Copier le code
let tempsPresence = 0;
let debutVisible = document.hidden ? null : Date.now();
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
// L'onglet se cache — accumuler le temps visible
if (debutVisible) {
tempsPresence += Date.now() - debutVisible;
debutVisible = null;
}
} else {
// L'onglet redevient visible — démarrer le chrono
debutVisible = Date.now();
}
});
// Envoyer le temps de présence au serveur avant de quitter
window.addEventListener('beforeunload', () => {
if (debutVisible) tempsPresence += Date.now() - debutVisible;
navigator.sendBeacon('/api/analytics', JSON.stringify({ tempsPresence }));
});
Compatibilité et cas particuliers
La Page Visibility API est disponible dans tous les navigateurs modernes sans préfixe. Quelques comportements à connaître :
- Fenêtre minimisée :
document.hiddenpasse àtruequand la fenêtre du navigateur est minimisée, même si l'onglet est actif. - Écran verrouillé : sur mobile, verrouiller l'écran déclenche
visibilitychangeavechidden = true. - Throttling des timers : les navigateurs ralentissent déjà
setTimeoutetsetIntervalquand une page est cachée. La Page Visibility API permet d'aller plus loin en les arrêtant complètement.
Par carabde | Mis à jour le 17 avril 2026