À propos de ce cours
Apprenez à utiliser l'API Web Storage : différences entre localStorage et sessionStorage, méthodes setItem, getItem, removeItem et clear, enregistrement d'objets et de tableaux avec JSON, fonctions robustes (valeur par défaut, JSON abîmé, stockage plein, expiration), exemple complet de préférences et de brouillon, synchronisation des onglets avec l'événement storage, choix entre cookies et Web Storage, limites et sécurité. Avec des activités corrigées.
-
Objectifs
- Utiliser l'API Web Storage :
localStorageetsessionStorage. - Enregistrer des objets et des tableaux grâce à
JSON.stringify()etJSON.parse(). - Écrire un code robuste : valeurs par défaut, données abîmées, stockage plein.
- Synchroniser plusieurs onglets avec l'événement
storageet connaître les limites de sécurité.
- Utiliser l'API Web Storage :
-
L'API Web Storage
- Le navigateur met à disposition deux objets qui fonctionnent exactement de la même façon : un dictionnaire de paires
clé → valeur, où clés et valeurs sont des chaînes de caractères. localStorage: les données sont permanentes. Elles restent après la fermeture de l'onglet, du navigateur, et même après un redémarrage de l'ordinateur, jusqu'à ce que le site ou l'utilisateur les efface.sessionStorage: les données sont propres à un onglet. Elles résistent à un rechargement de la page, mais disparaissent à la fermeture de l'onglet. Deux onglets ouverts sur le même site ont chacun leursessionStorage.- Les données sont rangées par origine (protocole + domaine + port) :
http://127.0.0.1:5500ethttp://localhost:5500ont des stockages différents. - Capacité : environ 5 Mo par origine, soit plus de mille fois un cookie. Contrairement aux cookies, rien n'est envoyé au serveur.
- Le navigateur met à disposition deux objets qui fonctionnent exactement de la même façon : un dictionnaire de paires
-
Les méthodes
| Instruction | Effet |
|---|---|
localStorage.setItem("theme", "sombre") | Enregistre (ou remplace) une valeur |
localStorage.getItem("theme") | Lit une valeur ; renvoie null si la clé n'existe pas |
localStorage.removeItem("theme") | Supprime une clé |
localStorage.clear() | Supprime toutes les clés du site |
localStorage.length | Nombre de clés |
localStorage.key(0) | Nom de la clé n° 0 (pour parcourir le stockage) |
localStorage.setItem("prenom", "Amira");
console.log(localStorage.getItem("prenom")); // "Amira"
console.log(localStorage.getItem("age")); // null : clé absente
// Parcourir toutes les clés
for (let i = 0; i < localStorage.length; i++) {
const cle = localStorage.key(i);
console.log(cle, "→", localStorage.getItem(cle));
}
- Pour les voir : DevTools → Application → Storage → Local storage ou Session storage. Vous pouvez y modifier et supprimer les valeurs.
Tout est texte : le piège n° 1
localStorage.setItem("note", 15);
localStorage.getItem("note") + 1; // "151" : c'est une chaîne !
localStorage.setItem("stagiaire", { nom: "Amira" });
localStorage.getItem("stagiaire"); // "[object Object]" : l'objet est perdu
- Pour stocker des nombres, des booléens, des objets ou des tableaux, on les convertit en texte JSON (chapitre 19) :
const taches = [
{ texte: "Réviser", faite: false },
{ texte: "Faire le TP", faite: true },
];
localStorage.setItem("taches", JSON.stringify(taches)); // objet → texte
const relues = JSON.parse(localStorage.getItem("taches")); // texte → objet
console.log(relues[1].faite); // true
- Attention aux types que JSON ne sait pas représenter : une
Datedevient une chaîne (à reconvertir avecnew Date(...)), une fonction ouundefineddisparaissent, uneMapdevient{}.
Un code robuste
- Le stockage peut poser trois problèmes : la clé n'existe pas encore (
null), le texte enregistré n'est pas du JSON valide (modifié à la main, ancienne version de l'application), ou le stockage est plein ou bloqué (erreurQuotaExceededError, réglages de confidentialité). - On regroupe donc l'accès dans deux fonctions qui ne plantent jamais,
stockage.js:
// Lire et écrire des objets JavaScript (et pas seulement du texte), sans jamais planter
function lire(cle, valeurParDefaut = null, stockage = localStorage) {
try {
const brut = stockage.getItem(cle);
return brut === null ? valeurParDefaut : JSON.parse(brut);
} catch {
return valeurParDefaut; // JSON abîmé ou stockage bloqué
}
}
function ecrire(cle, valeur, stockage = localStorage) {
try {
stockage.setItem(cle, JSON.stringify(valeur));
return true;
} catch (erreur) {
console.error(`Impossible d'enregistrer « ${cle} » :`, erreur.name); // QuotaExceededError…
return false;
}
}
// Variante avec date d'expiration (le localStorage n'en a pas)
function ecrireAvecExpiration(cle, valeur, minutes) {
return ecrire(cle, { valeur, expire: Date.now() + minutes * 60 * 1000 });
}
function lireAvecExpiration(cle) {
const entree = lire(cle);
if (!entree || Date.now() > entree.expire) {
localStorage.removeItem(cle);
return null;
}
return entree.valeur;
}
lire()renvoie une valeur par défaut au lieu de planter ;ecrire()renvoiefalsesi l'enregistrement a échoué, pour pouvoir prévenir l'utilisateur.- Le paramètre
stockagepermet d'utiliser les mêmes fonctions avecsessionStorage. - Le
localStoragen'a pas de date d'expiration :ecrireAvecExpiration()en ajoute une en stockant la date limite avec la valeur.
-
Exemple complet : préférences et brouillon
La page mémorise la taille du texte et le thème choisis (pour toujours, dans le localStorage) et le message en cours d'écriture (pour l'onglet, dans le sessionStorage).
index.html:
<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8" />
<title>Préférences et brouillon</title>
<script src="stockage.js" defer></script>
<script src="preferences.js" defer></script>
</head>
<body>
<h2>Mes préférences (localStorage)</h2>
<label>
Taille du texte
<select id="taille">
<option value="16px">Normale</option>
<option value="20px">Grande</option>
<option value="24px">Très grande</option>
</select>
</label>
<label><input id="sombre" type="checkbox" /> Thème sombre</label>
<h2>Message au formateur (brouillon en sessionStorage)</h2>
<form id="form-message">
<textarea id="message" rows="5" cols="50" placeholder="Votre message…"></textarea>
<br />
<button type="submit">Envoyer</button>
<span id="etat"></span>
</form>
</body>
</html>
preferences.js:
const CLE_PREFS = "prefs";
const prefs = lire(CLE_PREFS, { taille: "16px", sombre: false });
const choixTaille = document.getElementById("taille");
const choixSombre = document.getElementById("sombre");
function appliquer() {
document.body.style.fontSize = prefs.taille;
document.body.style.background = prefs.sombre ? "#1e1e1e" : "";
document.body.style.color = prefs.sombre ? "#eee" : "";
choixTaille.value = prefs.taille;
choixSombre.checked = prefs.sombre;
}
choixTaille.addEventListener("change", () => {
prefs.taille = choixTaille.value;
ecrire(CLE_PREFS, prefs);
appliquer();
});
choixSombre.addEventListener("change", () => {
prefs.sombre = choixSombre.checked;
ecrire(CLE_PREFS, prefs);
appliquer();
});
// Un autre onglet a modifié les préférences : on se met à jour
window.addEventListener("storage", (event) => {
if (event.key === CLE_PREFS) {
Object.assign(prefs, lire(CLE_PREFS, {}));
appliquer();
}
});
appliquer();
// ---------- Brouillon : survit à un rechargement, disparaît à la fermeture de l'onglet
const zone = document.getElementById("message");
zone.value = sessionStorage.getItem("brouillon") ?? "";
zone.addEventListener("input", () => {
sessionStorage.setItem("brouillon", zone.value);
document.getElementById("etat").textContent = "Brouillon enregistré";
});
document.getElementById("form-message").addEventListener("submit", (event) => {
event.preventDefault();
sessionStorage.removeItem("brouillon");
zone.value = "";
document.getElementById("etat").textContent = "Message envoyé ✅";
});
- Les préférences sont rangées dans un seul objet sous une seule clé : plus simple à lire, à écrire et à faire évoluer.
- Le brouillon est enregistré à chaque frappe (événement
input) : un rechargement accidentel ne fait rien perdre. Il est effacé après l'envoi.
Synchroniser plusieurs onglets : l'événement storage
- Quand un onglet modifie le
localStorage, les autres onglets ouverts sur le même site reçoivent l'événementstorage(l'onglet qui a fait la modification ne le reçoit pas). - L'événement contient
key(la clé modifiée, ounullaprès unclear()),oldValue,newValueeturl(la page à l'origine du changement). - Dans l'exemple, changez la taille du texte dans un onglet : le second onglet se met à jour tout seul. On relit la valeur avec
lire(), qui ne plante pas si elle est abîmée. - Usages : déconnexion dans tous les onglets, panier ou liste de tâches tenus à jour partout.
localStorage, sessionStorage ou cookie ?
| Besoin | Choix |
|---|---|
| Thème, langue, liste de tâches, panier d'une boutique sans compte | localStorage |
| Brouillon d'un formulaire, étape en cours, filtre choisi dans un onglet | sessionStorage |
| Donnée que le serveur doit recevoir à chaque requête (session, langue de la page générée) | Cookie |
| Beaucoup de données, des fichiers, des recherches rapides, le mode hors ligne | IndexedDB (leçon suivante) |
Limites et sécurité
- Rien n'est secret. L'utilisateur peut lire et modifier son stockage, et tout script de la page aussi : une faille XSS permet de voler tout le
localStorage. N'y mettez jamais de mot de passe, de numéro de carte ni de jeton d'authentification de longue durée (préférez un cookieHttpOnlyposé par le serveur). - Ne faites pas confiance aux données relues: vérifiez leur format avant de les utiliser, et affichez-les avec
textContent. - API synchrone: chaque lecture ou écriture bloque la page un instant. C'est négligeable pour quelques Ko, mais pas pour plusieurs Mo : utilisez alors IndexedDB.
- Données effaçables: l'utilisateur peut vider ses données de navigation, et la navigation privée efface tout à la fermeture. Safari supprime aussi, après 7 jours sans visite, les données des sites consultés sans interaction. Gardez toujours une copie importante sur un serveur ou proposez un export.
- Versionnez vos clés (
"todo:taches:v1") : si la structure des données change, l'application saura reconnaître et convertir les anciennes données.
Activités
Activité 1 : manipulations dans la console
- Sur la page de votre choix (en local), enregistrez votre prénom, votre groupe et votre moyenne. Relisez la moyenne et ajoutez-lui 1 : quel résultat obtenez-vous ? Corrigez avec
Number(). - Enregistrez un tableau de trois matières avec
JSON.stringify(), puis relisez-le et affichez la deuxième matière. - Fermez l'onglet, rouvrez la page : que reste-t-il dans le
localStorage? dans lesessionStorage?
Activité 2 : préférences et synchronisation
- Dans le dossier
storagede votre dépôtjs-stockage-navigateur, reproduisez l'exemple complet. - Ouvrez la page dans deux onglets et vérifiez la synchronisation. Ouvrez-la dans une fenêtre de navigation privée : que constatez-vous ?
- Ajoutez un bouton « Réinitialiser mes préférences » qui supprime la clé et rétablit les valeurs par défaut.
Activité 3 : formulaire d'inscription en plusieurs étapes
- Créez un formulaire en 3 étapes (identité, coordonnées, choix de la formation). Le contenu des champs et l'étape en cours sont conservés dans le
sessionStorage: un rechargement ramène au même endroit. - À la validation finale, effacez tout le brouillon.
Activité 4 : questions
- Que renvoie
localStorage.getItem("inconnue")? - Pourquoi entoure-t-on
JSON.parse()d'untry / catch? - Un développeur stocke le jeton de connexion de l'utilisateur dans le
localStorage. Quel est le risque ?
Corrigés. Activité 1 :
"15" + 1 donne "151"; avec Number(localStorage.getItem("moyenne")) + 1 on obtient 16 ; après réouverture, le localStorage est intact et le sessionStorage est vide (nouvel onglet). Activité 2 : en navigation privée, on part d'un stockage vide, effacé à la fermeture de la fenêtre. Activité 4 : null; le texte enregistré peut ne pas être du JSON valide (modifié à la main, ancienne version), et JSON.parse() lèverait une erreur qui bloquerait toute l'application ; une faille XSS permettrait à un script malveillant de lire le jeton et de se faire passer pour l'utilisateur.À retenir
setItem(),getItem(),removeItem(),clear(); les valeurs sont des chaînes.- Objets et tableaux :
JSON.stringify()pour écrire,JSON.parse()(dans untry / catch) pour lire. localStorage: permanent et partagé entre onglets ;sessionStorage: un par onglet, effacé à sa fermeture.- L'événement
storagesynchronise les onglets ; aucune donnée sensible dans le stockage.
Sources : MDN Web Docs, web.dev, CNIL, INPDP.