Gli oggetti web storage localStorage
e sessionStorage
permetto di salvare le coppie key/value nel browser. Ciò che è interessante è che i dati rimangono memorizzati anche in seguito al ricaricamento della pagina (per sessionStorage
) e anche in seguito a un riavvio del browser (per localStorage
). Vedremo come.
Abbiamo già a disposizione i cookies. Perché usare altri oggetti?
- Rispetto ai cookies, gli oggetti web storage non vengono inviati al server con ogni richiesta. Per questo motivo, possiamo archiviarne molti di più. La maggior parte dei browser permette almeno 2 megabytes di dati (o più) e possiedono impostazioni per configurare questa scelta.
- Inoltre, il server non può manipolare la memorizzazione degli oggetti tramite HTTP headers. Tutto viene fatto in JavaScript.
- L’archiviazione è legata alla sorgete (origin) (domain/protocol/port triplet). Questo perché, protocolli differenti o sotto domini deducono diverse archiviazioni a oggetti e non possono accedere ai dati tra di loro.
Le due archiviazioni a oggetti propongono stessi metodi e proprietà:
setItem(key, value)
: memorizza la coppia key/value.getItem(key)
: lettura del valore dalla key.removeItem(key)
: rimuove la key, ed il relativo value.clear()
: rimuove tutti gli elementi.key(index)
: lettura della key all’indiceindex
.length
: il numero di oggetti archiviati.
Come potete vedere, è simile alla collezione Map
(setItem/getItem/removeItem
), mantiene comunque l’ordine degli elementi e permette il loro accesso tramite indice con key(index)
.
Vediamo come funziona.
localStorage demo
Le caratteristiche principali di localStorage
sono:
- Condivisione tra le tabs e finestre provenienti dalla stessa origine.
- I dati non scadono. Rimangono in seguito a un riavvio del browser o dell’intero sistema operativo.
Per esempio, il seguente esempio:
localStorage.setItem('test', 1);
…E chiudiamo/apriamo il browser o semplicemente apriamo la stessa pagina in una finestra diversa, possiamo ottenere il risultato atteso in questo modo:
alert( localStorage.getItem('test') ); // 1
Dobbiamo solo essere nello stesso punto di partenza (domain/port/protocol), l’URL di destinazione può essere differente.
Il localStorage
è condiviso tra tutte le finestre con la stessa provenienza, quindi se impostiamo i dati in una finestra, il cambiamento diventa visibile anche nelle altre schede.
Accesso in stile oggetto
Possiamo usare la stessa sintassi di lettura/scrittura degli oggetti per accedere agli elementi, in questo modo:
// imposta un nuovo valore
localStorage.test = 2;
// legge il valore
alert( localStorage.test ); // 2
// rimuove il valore
delete localStorage.test;
Questo è permesso per ragioni storiche, e principalmente funziona, ma generalmente non è raccomandato, perché:
-
Se la key è generata dall’utente, può essere qualsiasi cosa, come
length
otoString
, o un altro metodo integrato dilocalStorage
. In questo casogetItem/setItem
funziona normalmente, mentre l’accesso in stile oggetto non funziona:let key = 'length'; localStorage[key] = 5; // Error, can't assign length
-
C’è un evento
storage
, che viene emesso quando modifichiamo dati. Questo evento viene per gli accessi in stile oggetto. Vedremo più avanti nel capitolo.
Cicli sulle keys
Come abbiamo visto, i metodi forniscono funzionalità get/set/remove. Ma come otteniamo tutti i valori o keys salvate?
Sfortunatamente, gli oggetti archiviati non sono iterabili.
Una soluzione sarebbe quella di eseguire un loop su di loro come un array:
for(let i=0; i<localStorage.length; i++) {
let key = localStorage.key(i);
alert(`${key}: ${localStorage.getItem(key)}`);
}
Un altro modo è quello di usare un loop for key in localStorage
, come facciamo con oggetti regolari.
L’iterazione avverrà sulle keys, ma anche sugli outputs di campi associati ad alcune funzioni built-in di cui non abbiamo bisogno:
// cattivo esempio
for(let key in localStorage) {
alert(key); // mostra getItem, setItem e altre funzioni integrate
}
… dunque dobbiamo filtrare i campi dal prototype con il controllo hasOwnProperty
:
for(let key in localStorage) {
if (!localStorage.hasOwnProperty(key)) {
continue; // salta keys come "setItem", "getItem" etc
}
alert(`${key}: ${localStorage.getItem(key)}`);
}
…oppure possiamo ottenere le keys “proprie” con Object.keys
ed eseguire il loop su di loro se necessario:
let keys = Object.keys(localStorage);
for(let key of keys) {
alert(`${key}: ${localStorage.getItem(key)}`);
}
Un lavoro extra, poiché Object.keys
restituisce solo le keys che appartengono all oggetto, ignorando il prototype.
Solo stringhe
Da notare che sia key che i value devono essere stringhe.
Se ci fosse un altro tipo di dato, come un numero, o un object, verrebbe automaticamente convertito a stringa:
localStorage.user = {name: "John"};
alert(localStorage.user); // [object Object]
Possiamo usare JSON
per archiviare oggetti:
localStorage.user = JSON.stringify({name: "John"});
// successivamente
let user = JSON.parse( localStorage.user );
alert( user.name ); // John
Inoltre è possibile convertire a stringa l’intero archivio di oggetti, per esempio per motivi di debugging:
// aggiunte le opzioni di formattazione a JSON.stringify per rendere object migliore
alert( JSON.stringify(localStorage, null, 2) );
sessionStorage
L’oggetto sessionStorage
è usato molto meno spesso del localStorage
.
Proprietà e metodi sono gli stessi, ma è più limitato:
- La
sessionStorage
esiste solo all’intero della tab del browser corrente.- Un’altra tab con la stessa pagina avrà un archiviazione differente.
- Viene comunque condivisa tra iframes nella stessa tab (assumendo che la loro provenienza sia la stessa).
- I dati sopravvivono al refresh della pagina, ma non alla chiusura/apertura della tab.
Vediamo come si comporta.
Esegui questo codice…
sessionStorage.setItem('test', 1);
…Poi ricarica la pagina. Pra potrai comunque ottenere i dati:
alert( sessionStorage.getItem('test') ); // dopo il refresh: 1
…Ma se apri la stessa pagina in un’altra tab, e provi di nuovo, dal codice otterrai null
, ovvero “non è stato trovato nulla”.
Questo perché sessionStorage
è legato non solo all’origine, ma anche alla tab del browser. Per questo motivo, sessionStorage
è usato sporadicamente.
Evento di storage
Quando i dati vengono aggiornati in localStorage
o sessionStorage
, un evento di storage viene emesso, con le seguenti proprietà:
key
: La key che è stata modificata (null
se è stato invocato.clear()
).oldValue
: Il vecchio valore (null
se la chiave è nuova).newValue
: il nuovo valore (null
se la chiave è sta rimossa).URL
: L’URL del documento in cui è avvenuto l’aggiornamento.storageArea
: se l’aggiornamento è avvenuto inlocalStorage
osessionStorage
.
La cosa importante è: l’evento si attiva in tutti gli oggetti window
dove l’archivio è accessibile, ad eccezione per quello in cui è stato causato.
Elaboriamo.
Immaginate di avere due finestre aperte con lo stesso sito all’interno. Quindi localStorage
è condiviso tra le due.
Dovresti aprire questa pagina in due browser per testare il seguente codice.
Se entrambe le finestre sono connesse a window.onstorage
, allora reagiranno agli aggiornamenti che accadono in una delle due.
// attiva un aggiornamento fatto dallo stesso archivio degli altri documenti
window.onstorage = event => { // identico a window.addEventListener('storage', event => {
if (event.key != 'now') return;
alert(event.key + ':' + event.newValue + " at " + event.URL);
};
localStorage.setItem('now', Date.now());
Notare che l’evento contiene: event.URL
– l’URL del documento in cui i dati sono stati aggiornati.
Inoltre, event.storageArea
contiene lo storage object – l’evento è lo stesso per entrambi sessionStorage
e localStorage
, quindi event.storageArea
si rivolge a quello che è stato modificato. Potremmo anche impostare qualcosa all’interno, per “rispondere” al cambiamento.
That allows different windows from the same origin to exchange messages.
I browser moderni supportano Broadcast channel API, un API speciale per comunicazione inter-finestra provenienti dalla stessa sorgente, possiede molte più funzionalità ma è meno supportata. Esistono librerie che sostituiscono quella API, basate su localStorage
, che lo rendono disponibile ovunque.
Riepilogo
Gli oggetti web storage localStorage
e sessionStorage
permettono di archiviare key/value nel browser.
- Sia
key
evalue
devono essere stringhe. - Il limite è 2mb+, dipende dal browser.
- Non scadono.
- I dati sono legati alla sorgente (domain/port/protocol).
localStorage |
sessionStorage |
---|---|
Condivise tra tutte le tabs e finestre provenienti dalla stessa sorgente | Visibile all’interno di una tab del browser, incluso iframes della stessa origine |
Sopravvivono al riavvio del browser | Sopravvivono al refresh della pagina (ma non alla chiusura della tab) |
API:
setItem(key, value)
: archivia coppia key/value .getItem(key)
: legge il valore dalla chiave.removeItem(key)
: rimuove la chiave con il suo valore.clear()
: cancella tutto.key(index)
: legge il valore all’indiceindex
.length
: il numero di oggetti archiviati.- Utilizza
Object.keys
per ottenere tutte le chiavi. - Accediamo alle keys come proprietà degli oggetti, in questo caso l’evento
storage
non verrà emesso.
Evento di storage:
- Emesso su chiamata di
setItem
,removeItem
,clear
. - Contiene tutti i data riguardo l’operazione (
key/oldValue/newValue
), il documentoURL
e lo storage objectstorageArea
. - Emesso su tutti gli oggetti
window
che hanno accesso all’archivio eccetto quello da cui è stato generato (all’interno di una tab persessionStorage
, globalmente perlocalStorage
).
Commenti
<code>
, per molte righe – includile nel tag<pre>
, per più di 10 righe – utilizza una sandbox (plnkr, jsbin, codepen…)