Using the PageVisibility API

HTML5 Rocks

Introduzione

Come sviluppatori web, abbiamo la tendenza ad entusiasmarci per le nuove tecnologie che ci consento di creare pagine web sempre più avvincenti e interattive. Grafica 3D con WebGL? Senz'altro. Capacità audio avanzate con WebAudio? Certamente. Applicazioni per la collaborazione in tempo reale che utilizzano webcam e microfono? Ci sono! Meno eccitanti, anche se ugualmente importanti, sono le tecnologie che ci permettono di implementare applicazioni che vengano eseguite in modo più efficiente e forniscano un'esperienza d'uso complessiva migliore. Questo è il campo in cui API come PageVisibility entrano in gioco. La API Page Visibility esegue una funzione semplice ma importante - consente alla tua applicazione di sapere quando una pagina è visibile all'utente. Questa informazione permette di creare pagine web il cui comportamento è diverso quando non sono visibili all'utente. Considera alcuni esempi:

  • Una pagina web che preleva le informazioni da un server può abbassare la sua frequenza di aggiornamento quando non viene visitata attivamente
  • Una pagina che mostra un carosello di immagini in rotazione o un contenuto video/audio può andare in pausa fin quando l'utente non visualizza di nuovo la pagina
  • Una applicazione può decidere di mostrare notifiche all'utente solo quando nascosta

All'inizio, questa API non sembrava molto utile al di là dei vantaggi per l'utente, ma considerando l'enorme aumento del web browsing su dispositivi mobili, qualunque cosa aiuti a risparmiare batteria è diventato di fondamentale importanza. Utilizzando l'API PageVisibility, il tuo sito può aiutare i dispositivi degli utenti a consumare di meno e durare di più.

La specifica dell'API, che al momento della scrittura di questo articolo si trova in fase di Candidate Recommendation, fornisce sia le proprietà per rilevare lo stato di visibilità del documento, nonchè un evento per rispondere ai cambiamenti di visibilità. In questo tutorial andrò a coprire le basi della API e mostrerò come applicarla tramite alcuni esempi pratici (sentiti libero di consultarli direttamente se sei un tipo impaziente).

Proprietà di Visibilità del Documento

La versione attuale della specifica di PageVisibilityAPI definisce due proprietà per il documento: il booleano hidden e l'enumerativo visibilityState. La proprietà visibilityState al momento ha quattro possibili valori: "hidden", "visible", "prerender", e "unloaded".

Nota: queste proprietà attuamente sono fornite con il prefisso, quindi devi utilizzare le versioni con prefisso come "webkitHidden" e "webkitVisibilityState" fin quando la specifica non avrà raggiunto lo stato di Official Recommendation e i fornitori di browser non implementeranno le versioni senza prefisso.

Come ci si potrebbe aspettare, l'attributo hidden restituisce true quando il documento non è per niente visibile. Tipicamente, questo significa che il documento è minimizzato o in un tab in background, che il lockscreen del sistema operativo è attivo, e cos' via. L'attributo viene settato a falso se qualsiasi parte del documento è anche solo parzialmente visibile su almeno uno schermo. Inoltre, per adattarsi ai tool di accessibilità, l'attributo hidden può essere settato a false quando un tool come una lente di ingrandimento per lo schermo oscura completamente il documento, ma ne mostra una parte.

Gestire i prefissi dei vendor

Al fine di mantenere l'attenzione sul codice invece che su tutti i prefissi specifici dei vendor, utilizzerò una funzione ausiliaria per isolare le specifiche dei browser.

function getHiddenProp(){
    var prefixes = ['webkit','moz','ms','o'];
    
    // if 'hidden' is natively supported just return it
    if ('hidden' in document) return 'hidden';
    
    // otherwise loop over all the known prefixes until we find one
    for (var i = 0; i < prefixes.length; i++){
        if ((prefixes[i] + 'Hidden') in document) 
            return prefixes[i] + 'Hidden';
    }

    // otherwise it's not supported
    return null;
}

Esempio di Proprietà del Documento

A questo punto possiamo scrivere una funzione multi-browser, isHidden(), per controllare se il documento è visibile.

function isHidden() {
    var prop = getHiddenProp();
    if (!prop) return false;
    
    return document[prop];
}

Per un'analisi più granulare della visibilità del documento, puoi utilizzare la proprietà visibilityState. Questa proprietà restituisce uno tra questi quattro valori:

  • "hidden": il documento è completamente nascosto alla vista
  • "visible": il documento è anche solo parzialmente visibile su almeno uno schermo
  • "prerender": il documento è caricato al di fuori dello schermo e non è visibile (questo valore è opzionale; non tutti i browser necessariamente lo supportano)
  • "unloaded": se il documento deve essere dismesso, allora sarà restituito questo valore (questo valore è opzionale; non tutti i browser necessariamente lo supportano)

L'Evento VisibilityChange

In aggiunta alle proprietà di visibilità, è presente un evento visibilitychange che viene notificato ogni volta che lo stato della visibilità di un documento cambia. Si può registrare un listener a questo evento direttamente all'interno del document:

Esempio di Evento

// use the property name to generate the prefixed event name
var visProp = getHiddenProp();
if (visProp) {
  var evtname = visProp.replace(/[H|h]idden/,'') + 'visibilitychange';
  document.addEventListener(evtname, visChange);
}

function visChange() {
   var txtFld = document.getElementById('visChangeText');

   if (txtFld) {
      if (isHidden())
         txtFld.value += "Tab Hidden!\n";
      else
         txtFld.value += "Tab Visible!\n";
   }
}

Gli effetti del codice riportato sopra si possono osservare nel campo di testo editabile qui sotto. Prova a nascondere il tab contenente questa pagina e osserva come cambia il contenuto del campo di testo:

(Se leggi scritto "PageVisibilityAPI not supported!", allora il tuo browser non supporta l'API in questione)

Esempi pratici

Riproduzione e interruzione di un video

Questo esempio mostra come utilizzare l'API PageVisibiliry per interrompere e avviare un video. Prova ad avviare la riproduzione del video qui sotto e a cambiare tab nel browser. Vedrai che (nei browser supportati) il video si interrompe e riparte al cambiare della visibilità del tab.

Questo è reso possibile intercettando l'evento visibilityChange e modificando conseguentemente lo stato del video:

window.addEventListener("load", function vidDemo() {
   sessionStorage.initialPlay = false;
   var vidElem = document.getElementById("video-demo");

   var visProp = getHiddenProp();
   if (visProp) {
      vidElem.addEventListener("play", function() {
         sessionStorage.initialPlay = true;
      });

      var evtName = visProp.replace(/[H|h]idden/,'') + 'visibilitychange';
      document.addEventListener(evtName, startStopVideo);
   }

   function startStopVideo() {
      if (document[visProp]) {
         vidElem.pause();
      }
      else if (vidElem.paused && sessionStorage.initialPlay == "true") {
         vidElem.play();
      }
   }
});

Visualizzazione delle notifiche solo quando un tab è nascosto

Le API W3C per le notifiche permettono di mostrare dei pop-up di notifica all'utente per ottenere la sua attenzione. Tuttavia, quando l'utente è già concentrato sulla pagina questa pratica può risultare fastidiosa. Utilizzando le API PageVisibility, si può decidere di mostrare le notifiche solo quando il tab è nascosto e non visualizzato.

Prova ad abilitare le notifiche cliccando sul pulsante "Enable Notifications". Poi clicca sul pulsante "Notify Me!" e abbandona il tab. Dopo 5 secondi, il codice controllerà se il tab è nascosto e mostrerà una notifica. Altrimenti, utilizzerà un alert standard.

(Per prima cosa premi qui per controllare che le notifiche siano abilitate)

Qui trovi il codice:

window.addEventListener("load", function notifyDemo() {
   var propName = "";
   var oNotify=null;
   var visProp = getHiddenProp();

   document.getElementById("notify-demo").addEventListener("click", function() {
      oNotify = null;
      if (window.webkitNotifications) {
         setTimeout(showNotification, 5000);

         if (window.webkitNotifications.checkPermission() == 0) { // 0 is PERMISSION_ALLOWED
            oNotify = window.webkitNotifications.createNotification('', 'Notification', 'You have been notified!');
         } 
      }
   });

   document.getElementById("notify-enable").addEventListener("click", function() {
      window.webkitNotifications.requestPermission();
   });

   function showNotification() {
      if (document[visProp] && window.webkitNotifications && oNotify) {
         oNotify.show();
      }
      else {
         alert("You have been notified!");
      }
   }
});

Rinviare Google Analytics se la pagina è pre-renderizzata

Alcuni browser, come ad esempio Google Chrome, hanno la possibilità di pre generare pagine web (qui puoi trovare altre informazioni sul pre-rendering di Chrome). Questo processo comporta il download di tutte le risorse necessarie al rendering della pagina - compresi tutti gli script contenuti nella stessa. Diversi siti di terze parti utilizzano Google Analytics per rilevare quando e quanto le loro pagine vengono visitate, ma questo conteggio può disallinearsi se una pagina viene pre-renderizzata ma mai realmente visualizzata dall'utente.

Per prevenire questo problema, si può utilizzare l'API PageVisibility per rilevare che la pagina è stata pre-renderizzata, omettendo in tal caso il conteggio della vista. Tipicamente una pagina che utilizza Google Analytics contiene codice come questo:

var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-XXXXX-X']);
_gaq.push(['_trackPageview']);

In questo caso, l'evento _trackPageview viene sempre registrato, anche se la pagina non è mostrata all'utente. Uno script che rileva il pre-rendering sarà del tipo:

var bHavePV = getHiddenProp();
var bInitialShow = false;
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-XXXXX-X']);

if (bHavePV) {
   document.addEventListener("visibilityChange", handleVisEvt);
}
else {
   _gaq.push(['_trackPageview']); // track page view normally when PageVisibility is not present
}

function handleVisEvt() {
   if (document.visibilityState == "prerender") {
      _trackEvent("pagedata", "prerender"); // might be interesting to keep track of pre-rendering
   }
   if (document.visibilityState == "visible" && !bInitialShow) {
      bInitialShow = true; // don't trigger this code again
      _gaq.push(['_trackPageview']);
   }
}

Riepilogo

Realizzare una buona web application comporta molto di più che implementare incredibili e accattivanti funzionalità che l'utente può osservare e con cui può interagire. Una applicazione davvero grandiosa utilizza in modo attento le risorse e l'attenzione dell'utente; la API Page Visibility è un pezzo importante di questo puzzle. Per saperne di più sullo sviluppo di applicazioni web attente al consumo di risorse, dai un'occhiata agli altri articoli sulle performance.

Riferimenti esterni

Comments

0