LocalStorage – Variables globales en un dominio (JavaScript)

TEORÍA

En HTML tenemos una variable «súper global» predefinida llamada «localStorage«, refiriéndome al término «súper global» como una variable que se comparte entre todos los scripts de Javascript (o dicho de otra forma, se comparte para todas las pestañas del navegador). Funciona como un conjunto de pares «clave-valor».

Se comparte entre pestañas

Lógicamente no se comparte para todas las pestañas, en realidad se comparte para todas las pestañas del mismo dominio para evitar que código JavaScript de otros dominios pueda acceder/manipular datos que no sean suyos.

Funciona como una cookie

Además, esta variable súper global se mantiene al cerrar la sesión (la pestaña o incluso el navegador). Funciona como una cookie pero, en teoría, más segura y puede llegar a almacenar más información (El estándar son unos 4KB); por ende hay que entender que lo almacenado en esta variable súper global sólo afecta al navegador en la que está siendo utilizado (no se almacena en algún archivo del SO del cual lean todos los navegadores ni nada parecido).

Eleva eventos de cambio a todas las pestañas

También tiene un evento que se eleva al elemento «window» del DOM que se llama «storage» que nos avisa de que la variable «localStorage» ha cambiado de valor, e indicándonos la clave que ha cambiado y el valor nuevo y el antiguo de la misma.

window.addEventListener('storage', function(e){...});

Este evento es global a todas las pestañas del mismo dominio (excepto para la propia pestaña que provocó el cambio del valor de la variable «súper global»).

Sólo funciona en dominios

El «localStorage» pertenece al grupo conocido como WebStorage y sólo puede utilizarse cuando la página HTML funciona bajo algún dominio (aunque sea «localhost»), es decir, no se puede utilizar directamente abriendo un archivo HTML desde alguna carpeta. Esto es lógico ya que, como hemos dicho, el ámbito de uso es por dominio.

El error de máximo tamaño alcanzado no es estándar

Detectar el error cuando se alcanza el máximo tamaño que puede almacenar «localStorage» no es estándar entre los navegadores (aunque la mayoría coinciden en el código de error 22 y nombre de error «QuotaExceededError»), en cualquier caso habría que controlarlo:

  • En Firefox:
    • Código de error = 1014
    • Nombre = NS_ERROR_DOM_QUOTA_REACHED
  • En Google Chrome, Safari, IE, Edge:
    • Código de error = 22
    • Nombre = QuotaExceededError

PRÁCTICA

Vamos a contar cuántas veces se ha pulsado un botón y transmitir esa información al resto de pestañas del navegador.

También vamos a reiniciar el contador de pulsaciones y a provocar que se llene el «localStorage» para saber cómo se comporta en cada navegador.

Lo primero, una página HTML muy sencilla con los 3 botones (aumentar contador, borrar el contador y llenar la memoria):

<body>
    <button onclick="incrementClickCounter()">Clicks++</button>
    <br>
    <button onclick="clearClickCounter()">Reiniciar contador</button>
    <br><br>
    <button onclick="stressTest()">Llenar localStorage</button>
<div id="result"></div>
<p>Abre dos pestañas para ver que se actualiza el contenido (el contador de clicks) en ambas</p>
<p>Pulsa en la prueba de estrés para provocar que se llene el localstorage</p>
</body>

Y ahora el código JS.

¿Está definido «localStorage»?

Primero hay que pasar el preprotocolo para saber si podemos usar «localStorage«, para ello, si el objeto «Storage» no existe, significa que el navegador no lo implementa; y si lo implementa pero «localStorage» es nulo, significa que no estamos usando el script dentro de un dominio (esto es, estamos usando una IP, localhost o abriendo directamente el fichero local con el navegador).

// El navegador no puede usar "localStorage"
if(typeof(Storage) === "undefined") 
  alert("Sorry, your browser does not support web storage");

// El script JS no está detrás de un dominio
// (se ha abierto directamente desde el sistema de archivos
// y no a través de un servidor HTTP)
if(localStorage==null) 
  alert("Only accesible through HTTP server (that\'s it, on a domain)");

Una vez que nos aseguramos de que localStorage está operativo pasamos a las siguientes tareas.

Modificar el valor de una clave

En nuestro caso queremos sumar la unidad a la clave «clickcount» (a menos que no exista, en cuyo caso le asignamos la unidad), y tras ello, mostrarlo en pantalla:

//Asignar un valor a una clave
localStorage.<CLAVE> = <VALOR>;

function incrementClickCounter() {
    ...
    if(localStorage.clickcount) {
        localStorage.clickcount = Number(localStorage.clickcount)+1;
    } else {
        localStorage.clickcount = 1;
    }
    showClickCounter(localStorage.clickcount);
}

function showClickCounter(value){
    if(value==null){
        document.getElementById("result").innerHTML = '';
    }else{
        document.getElementById("result").innerHTML = "You have clicked the button " + value + " time(s) in this session.";
    }
}
Recibir eventos de cambio en las pestañas

Cuando se modifique el valor de una clave, se elevará un evento llamado «storage» asociado al elemento «window» del DOM.

Este evento sólo es recogido si el causante de la modificación no ha sido la propia pestaña (Si tenemos dos pestañas A y B, y A modifica el valor, el evento sólo será capturado en la pestaña B) menos en IE, que recoge el evento en todas las pestañas.

El evento indica 3 valores importantes:

  • La clave que ha cambiado
  • El valor antiguo de la clave
  • El valor nuevo de la clave

Así pues, recogemos el evento y lo mostramos en pantalla (vamos a usar el mismo script JS para todas las pestañas).

function showClickCounter(value){
    if(value==null){
        document.getElementById("result").innerHTML = '';
    }else{
        document.getElementById("result").innerHTML = "You have clicked the button " + value + " time(s) in this session.";
    }
}
Limpiar una clave

La idea es eliminar la clave del localStorage con un método propio:

function clearClickCounter(){
    localStorage.removeItem("clickcount");
    showClickCounter(localStorage.clickcount);
}
Controlar el error de tamaño máximo

Cuando se alcanza el tamaño máximo de almacenamiento del «localStorage» se eleva un error que puede variar según el navegador en el que se produzca.

Para ver qué código y qué nombre está devolviendo el error, se captura en un try-catch y lo mostramos en la consola.

Para provocar el error, añadimos un caracter en el «localStorage» en un bucle while infinito hasta que se capture el error. Debería llegar a unos 4096 KB de almacenamiento:

function stressTest(){
    // STRESS_DATA is cleared?
    if(localStorage.STRESS_DATA==null)
        console.log('localStorage.STRESS_DATA is empty');
    else
        console.log('localStorage.STRESS_DATA is NOT empty')
    // "data" should reach 4096K
    var data = "a";
    var i = 0;
    while(true){
        try { 
            localStorage.setItem("STRESS_DATA", data);
            data += data;
        }catch(e) {
            console.log(e);
            var storageSize = Math.round(JSON.stringify(localStorage).length / 1024);
            console.log("LIMIT REACHED: (" + i + ") " + storageSize + "K");
            console.log(e.code);
            console.log(e.name);
            //Google Chrome
            //Safari
            //IE, Edge
            if(e.code == 22 && e.name== "QuotaExceededError"){
                alert("localStorage (WebStorage): Cannot store more data, limit reached");
            //Firefox
            }else if(e.code == 1014 && e.name == "NS_ERROR_DOM_QUOTA_REACHED"){
                alert("localStorage (WebStorage): Cannot store more data, limit reached");
            }
            break;
        }
        i++;
    }
    localStorage.removeItem("STRESS_DATA");
}

DESCARGA EL EJEMPLO

Puedes descargar el ejemplo completo aquí.

Para testearlo debes abrir dos pestañas del archivo «webstorage.html» y ver cómo se actualiza el contador de clicks en ambas pestañas.

 

Deja un comentario