Avez-vous une question? (216) 97 656 803 hajjriadh@gmail.com
JavaScript

localStorage et sessionStorage

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 ro...

0 chapitres 0 QCM Accès gratuit
Dans cet article

À 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.
  1. Objectifs

    • Utiliser l'API Web Storage : localStorage et sessionStorage.
    • Enregistrer des objets et des tableaux grâce à JSON.stringify() et JSON.parse().
    • Écrire un code robuste : valeurs par défaut, données abîmées, stockage plein.
    • Synchroniser plusieurs onglets avec l'événement storage et connaître les limites de sécurité.
  2. 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 leur sessionStorage.
    • Les données sont rangées par origine (protocole + domaine + port) : http://127.0.0.1:5500 et http://localhost:5500 ont 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.
  3. Les méthodes

  4. InstructionEffet
    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.lengthNombre 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 Date devient une chaîne (à reconvertir avec new Date(...)), une fonction ou undefined disparaissent, une Map devient {}.
  • 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é (erreur QuotaExceededError, 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() renvoie false si l'enregistrement a échoué, pour pouvoir prévenir l'utilisateur.
    • Le paramètre stockage permet d'utiliser les mêmes fonctions avec sessionStorage.
    • Le localStorage n'a pas de date d'expiration : ecrireAvecExpiration() en ajoute une en stockant la date limite avec la valeur.
    1. Exemple complet : préférences et brouillon

    2. 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énement storage (l'onglet qui a fait la modification ne le reçoit pas).
    • L'événement contient key (la clé modifiée, ou null après un clear()), oldValue, newValue et url (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.
  • BesoinChoix
    Thème, langue, liste de tâches, panier d'une boutique sans comptelocalStorage
    Brouillon d'un formulaire, étape en cours, filtre choisi dans un ongletsessionStorage
    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 ligneIndexedDB (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 cookie HttpOnly posé 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 le sessionStorage?

    Activité 2 : préférences et synchronisation

    • Dans le dossier storage de votre dépôt js-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'un try / 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 un try / catch) pour lire.
    • localStorage: permanent et partagé entre onglets ; sessionStorage: un par onglet, effacé à sa fermeture.
    • L'événement storage synchronise les onglets ; aucune donnée sensible dans le stockage.

    Sources : MDN Web Docs, web.dev, CNIL, INPDP.