Page Visibility API: optimizar desempeño de web apps

Seguimos avanzando hacia la web. Los sistemas tradicionales centralizados están siendo desplazados por sistemas virtuales o plataformas en “la nube”, o simplemente migrando hacia servidores externos. Ya sea para reducir costos, mayor accesibilidad, distribución, expansión, etc. Cualquiera que sea le motivo, ha mostrado ser el futuro-presente.

Ahora, ¿que ha pasado con muchos sistemas de información tradicionalmente diseñados para ambientes locales al momento de migrar a la web? la respuesta es simple, toman mucho tiempo en realizarse. Generalmente debido a que en su diseño original muchos sistemas y aplicaciones consumen muchos recursos. Todavía hoy vemos sistemas virtuales online que consumen mucho recurso, principalmente los CRM’s, ERP’s y toda plataforma de gestión corporativa. Este tipo de aplicaciones consumen mucho recursos debido a varios factores. Por ejemplo, todos los módulos se cargan aún sin ser utilizados, como las librerías javascript o conexiones a base de datos. Otro ejemplo es la ejecución de procesos.

Es este último el factor más común de consumo de recursos aún sistemas o aplicaciones pequeñas. Page Visibility API de HTML5 puede ayudar a reducir esa carga de procesos.

Previamente habíamos hablado de las notificaciones en el navegador gracias a HTML5 y Javascript, y en ese momento les mencioné el tema de Page Visibility como otra forma de para que utilizar las notificaciones en el navegador.

Page Visibility permite conocer el estado de una página si está visible o no. Para ser más exactos, permite saber si un tab del navegador está activo. Cuando un usuario abre varias paginas a la vez en el navegador, cada una es abierta en una pestaña o tab. Si el usuario cambia la visibilidad de las página o de las pestañas el API envía un evento llamado visibilitychange en relación a cada una de las pestañas.

El evento visibilitychange, lee y actualiza el valor de la variable del documento visibilityState que puede tener cuatro (4) valores asignados por el propio navegador:

  • visible: indicando que la pagina o tab es parcial o completamente visible al usuario.
  • hidden: que el contenido o tab no es visible o activa
  • prerender: que el contenido de la pagina está siendo generado. En este estado es considerado hidden
  • unloaded: que la página o contenido del tab está siendo cargado de la memoria. Este estado es presente en algunos navegadores por lo que es opcional.

El evento visibilitychange puede variar de navegador a navegador, por ejemplo, para IE (Internet Explorer) se denomina msvisibilitychange. Para otros navegadores, el evento en si mismo es ausente, pero se puede determinar el estado por medio de la propiedad ‘hidden’ del documento. En ambos casos el resultado es el mismo, retorna un valor booleano (true, false). Por eso es importante determinar si el navegador tiene soporte para el evento visibilitychange antes de utilizarlo. De igual forma, sabe si el navegador soporta eventos en tiempo real para poder conocer el cambio de estado del evento agregando el mismo al DOM (Document Object Model) que es como trabajan los navegadores cuando cargan una página web. Leen la página como un documento estructurado línea por línea.

Entonces, primero determinaremos el navegador que está utilizando el usuario y asignando los valores que este soporta para conocer el estado de la página o pestaña del navegador:

var hidden, visibilityChange; 
if (typeof document.hidden !== "undefined") { // Opera 12.10 and Firefox 18 o mayor 
  hidden = "hidden";
  visibilityChange = "visibilitychange";
} else if (typeof document.msHidden !== "undefined") {
  hidden = "msHidden";
  visibilityChange = "msvisibilitychange";
} else if (typeof document.webkitHidden !== "undefined") {
  hidden = "webkitHidden";
  visibilityChange = "webkitvisibilitychange";
}

Este ejemplo lo estoy replicando del sitio de developers de mozilla, ya que es simple de de entender y presenta la esencia del concepto de visibilitychange.

Lo que acabamos de hacer es definir dos variables (hidden y visibilitychange para los navegadores que no soporten el evento, de forma que no importa el navegador, podamos hacer uso del API, siempre que soporte hidden estate del documento. De igual forma, identificamos por medio de una bifurcación o condición ‘IF’, cual evento utilizar.

Lo segundo es saber si el navegador soporta addEventListener para hacer un proceso recurrente de “escuchar” si el evento o valor de retorno ha cambiado.

if (typeof document.addEventListener === "undefined" || typeof document[hidden] === "undefined") {
  console.log("This demo requires a browser, such as Google Chrome or Firefox, that supports the Page Visibility API.");
}

LO que hemos realizado con este código es preguntar al navegador si existe un evento del documento llamado addEventListener o el valor hidden están presentes en la página o pestaña del navegador que carga la página. De no existir, el valor será inderterminado (‘undefined’) o que no existe ninguno de los dos, ya que hemos preguntado si existe uno o el otro (||).

Ahora podemos ejecutar la acción deseada una vez se determine el estado de la pagina o tab del navegador. Vamos a cambiar le titulo del a página al usuario de la manera mas simple posible: document.title para cuando la página o tab este en segundo plano o estemos viendo otra página en un tab diferente.

Como utilizaremos la escucha de eventos del navegador, vamos a definir una función que realice la acción de emitir el alerta:

function handleVisibilityChange() {
  if (document[hidden]) {
      // Cambiamos el titulo de la página para saber su estado
    document.title = 'No visible';
  } else {
      // Cambiamos el titulo de la página para saber su estado
        document.title = 'Visible'; 
  }
}

Ahora vamos a poner todo junto de forma lógica para determinar:

  1. si el navegador soporta visibilityChange
  2. Si el navegador soporte addEventListener

Si integramos todo tenemos el siguiente código en nuestra página:

<!DOCTYPE html>
<html>
<head>
    <title>title</title>
    </head>
    <body>
        
    <script>

        var hidden, visibilityChange; 
        if (typeof document.hidden !== "undefined") { // Opera 12.10 and Firefox 18 o mayor 
          hidden = "hidden";
          visibilityChange = "visibilitychange";
        } else if (typeof document.msHidden !== "undefined") {
          hidden = "msHidden";
          visibilityChange = "msvisibilitychange";
        } else if (typeof document.webkitHidden !== "undefined") {
          hidden = "webkitHidden";
          visibilityChange = "webkitvisibilitychange";
        }

        function handleVisibilityChange() {
          if (document[hidden]) {
              // Cambiamos el titulo de la página para saber su estado
            document.title = 'No visible';
          } else {
              // Cambiamos el titulo de la página para saber su estado
            document.title = 'Visible'; 
          }
        }

        // Preguntamos si soporta addEventListener para poder ejecutar nuestra acción de alerta

        if (typeof document.addEventListener === "undefined" || typeof document[hidden] === "undefined") {
          console.log("Necesitas Google Chrome o Mozilla Firefox para utilizar Page Visibility API.");
        } else {
          // Aquí leemos o consultado el estado constantemente para ver si cambia  
          document.addEventListener(visibilityChange, handleVisibilityChange, false);

        }
    </script>
        
    </body>
</html>

Notarán que he puesto el código Javascript en el tag <body> y no en el tag <head>. Esto por simplicidad, lo puedes poner en cualquiera de los dos e igual funciona.

Si abre el documento con su navegador, recomiendo chrome o firefox, podrán ver como cambia el titulo del tab al cambiar de un tab a otro. Suponiendo que abrieron mas de una pagina en su navegador para los fines del ejemplo. También es importante hacer las pruebas en un servidor web y no abriendo el archivo de forma local, que aunque funciona igual, es recomendable siempre hacer las pruebas en el ambiente más real posible y ver el comportamiento.

Voila! todo set. Ahora puedes hacer tus propios eventos según el estado del tab o navegador y poder controlar el consumo de recursos de tu web app, página web e incluso sistemas de intranet o corporativos que estén desarrollados en ambiente web. Esto ayuda muchísimo a evitar esos ‘[Not responding]’ que vemos a veces cuando cargamos páginas que toman mucho tiempo o recursos en cargar.

Y se preguntarán, ¿cómo o para que utilizar el Page Visibility API? pues bien. Si están mostrando un video tutorial que se debe seguir paso por paso y quieran evitar ese ‘play’ y ‘pause’ que se debe hacer cuando se quiere realizar un ejercicio y seguirle el paso al video tutorial. O detener una consulta a un archivo remoto, o un archivo json con data variable ne tiempo real como un dashboard.

Un ejemplo real es como el sitio de música Pandora que pueden ver en el cover de este artículo que detiene la música si duras mucho tiempo sin ver la página y por ende no ves los anuncios ;). En este sitio web detienen la música y te muestran un mensaje preguntando si aún estas ahí escuchando. De lo contrario detienen ese consumo de ancho de banda de su servicio y lo dirigen a otro oyente. Lo mismo, con los anuncios, detienen los anuncios para así maximizar el ROI o garantizar que sus anuncios están siendo vistos por los usuarios.

Puedes ahora, utilizar este código y hacer tu propia versión de la acción o incluso combinarla con otros artículos como el de las notificaciones que les hablaba al principio.

CODE IS POETRY!