Use case courant : une app web où l’utilisateur doit garder un œil sur l’écran pendant plusieurs minutes sans interagir (navigation cartographique, suivi d’un objet/personne en temps réel, monitoring live d’un graphique). On utilise navigator.wakeLock.request('screen') pour empêcher la mise en veille automatique de l’écran.
async function enterTrackingMode() {
wakeLock = await navigator.wakeLock.request('screen');
// ... le reste : géoloc, polling, MAJ UI
}
Ça marche : tant que l’onglet est au premier plan, l’écran reste allumé.
Sauf quand l’utilisateur switche d’onglet ou verrouille manuellement, puis revient.
Symptôme
L’utilisateur ouvre l’app, active le mode « garde l’écran allumé », marche un peu, l’écran s’éteint après quelques secondes au lieu de rester allumé. Quand il re-déverrouille / revient à l’onglet, l’écran s’allume normalement mais s’éteint à nouveau dès qu’il arrête de toucher.
Aucune erreur console. La variable wakeLock est toujours définie (l’objet WakeLockSentinel existe encore). On a l’impression que c’est encore actif.
Cause
Le browser relâche automatiquement le wake lock dans plusieurs situations :
- l’onglet passe en
visibilitychange→hidden(utilisateur change d’onglet, verrouille l’écran, app va en arrière-plan) - le device entre en low power mode
- raisons internes du browser
C’est documenté mais facile à manquer : le WakeLockSentinel reste un objet JS valide, mais son .released === true. Le browser n’a aucune obligation de réveiller automatiquement le lock quand l’onglet redevient visible. Il faut le re-demander explicitement.
Et même si tu écoutes l’event release sur le sentinel pour mettre à jour ton flag, ça ne suffit pas si tu ne reconnectes pas le visibilitychange au re-acquire.
La bonne approche
Trois choses à câbler ensemble :
let wakeLock = null;
async function acquireWakeLock() {
if (!('wakeLock' in navigator)) return; // pas supporté
try {
wakeLock = await navigator.wakeLock.request('screen');
wakeLock.addEventListener('release', () => {
wakeLock = null; // (1) flag à jour quand le browser relâche
});
} catch (e) {
console.warn('Wake lock refusé:', e.message); // (2) NotAllowedError courant (pas de user gesture, etc.)
}
}
document.addEventListener('visibilitychange', async () => {
if (modeActif && document.visibilityState === 'visible' && !wakeLock) {
await acquireWakeLock(); // (3) ré-acquérir au retour foreground
}
});
async function exitTrackingMode() {
if (wakeLock) { try { await wakeLock.release(); } catch {} wakeLock = null; }
}
Les 3 pièces sont nécessaires :
- Listener
releasesur le sentinel pour mettrewakeLock = nullquand le browser le relâche tout seul → sans ça, ton check!wakeLockau retour foreground ment - Try/catch :
wakeLock.request()peut throwNotAllowedError(pas d’user gesture, page non visible, etc.) — sans catch, ton mode actif crashe silencieusement à la 1re tentative - Re-acquisition au
visibilitychange: sans ça, le wake lock relâché en arrière-plan ne revient jamais quand l’utilisateur retourne sur l’onglet
Lessons learned
- Le
WakeLockSentineln’est PAS un handle persistant — c’est un objet qui peut être en état « released » pendant que tu le tiens encore en variable. Toujours vérifier.releasedou maintenir un flag via le listener - Pas de re-acquisition automatique au retour foreground — c’est ton job de re-
request()auvisibilitychange. Le browser ne le fait pas pour toi par sécurité (pas de wake lock fantôme) - Le wake lock
request('screen')n’est dispo que sur secure origin (HTTPS ou localhost). En HTTP plain,navigator.wakeLockest absent → toujours guarder avec'wakeLock' in navigator - Safari iOS support depuis 16.4 — assez récent, prévoir un fallback graceful (le mode marche mais l’écran s’éteindra normalement)
- Tester avec un mock qui émet le
releaseevent : si ton mock met justereleased = truesans dispatcher, tu vas valider une logique de ré-acquisition qui ne marchera pas en prod. Émets vraiment l’event dans ton harness - Diagnostic en 1 ligne :
console.log('WL state:', wakeLock?.released, document.visibilityState)dans l’handlervisibilitychange— si tu voisreleased=trueau retourvisibleet pas de re-request, c’est l’angle mort
#web-apis #wake-lock #visibility-api #frontend #mobile #pwa