W3docs

JavaScript History API

En développement web moderne, créer des expériences utilisateur fluides implique souvent de manipuler l'historique du navigateur. L'API History JavaScript fournit les outils nécessaires.

Introduction à l'API History JavaScript

En développement web moderne, créer des expériences utilisateur fluides implique souvent de manipuler l'historique du navigateur. L'API History de JavaScript vous permet de lire et de modifier l'historique de session du navigateur — la liste des pages (et états d'URL) que l'utilisateur a visitées dans l'onglet actif. En utilisant cette API, les développeurs peuvent mettre à jour la barre d'adresse et la pile retour/suivant sans déclencher un rechargement complet de la page, ce qui est exactement ce dont les applications monopages (SPA) ont besoin pour se sentir rapides et natives.

Cette page couvre les trois méthodes principales (pushState, replaceState et les helpers de navigation), la façon de réagir à l'événement popstate, les erreurs courantes qui piègent les développeurs, ainsi que les bonnes pratiques pour utiliser l'API en production.

Pourquoi l'API History existe

Avant cette API, la seule façon de changer l'URL était de naviguer, ce qui rechargait l'intégralité du document. Les SPA affichent de nouvelles « pages » avec JavaScript, elles ont donc besoin que l'URL reste synchronisée sans rechargement — sinon le bouton retour, les favoris et les liens partageables ne fonctionnent plus.

L'API History résout ce problème en vous offrant un moyen de :

  • Ajouter une nouvelle entrée à la pile retour/suivant (pushState).
  • Modifier l'entrée courante en place (replaceState).
  • Réagir lorsque l'utilisateur parcourt cette pile avec les boutons retour/suivant (popstate).

Vous lisez les données de l'entrée courante via la propriété en lecture seule history.state, et history.length vous indique combien d'entrées se trouvent dans la pile de session. Pour construire les URL que vous passez à ces méthodes, l'objet URL est un compagnon utile.

L'objet history en un coup d'œil

L'API est exposée sur l'objet global window.history (vous pouvez écrire simplement history) :

MembreCe qu'il fait
history.pushState(state, title, url)Ajoute une nouvelle entrée à la pile d'historique et met à jour l'URL.
history.replaceState(state, title, url)Remplace l'entrée courante — aucune nouvelle entrée n'est créée dans la pile.
history.stateCopie en lecture seule de l'objet state de l'entrée courante.
history.lengthNombre d'entrées dans l'historique de session.
history.back()Recule d'une entrée (identique au bouton retour du navigateur).
history.forward()Avance d'une entrée.
history.go(n)Saute de n entrées (négatif = retour, positif = suivant).

Utilisation de l'API History dans les applications web

L'API History permet de naviguer entre différents états d'une application sans recharger la page. pushState() prend trois arguments — un objet state (toute valeur sérialisable), un title (ignoré par la plupart des navigateurs, donc passez une chaîne vide), et une url (résolue relativement à la page courante et doit avoir la même origine). Voici comment pousser un nouvel état :

<div>
    <button onclick="changeState()">Go to New State</button>
</div>

<script>
    // Function to change state
    function changeState() {
        const newState = { id: 'newState' };
        // Push a new state to the history stack
        window.history.pushState(newState, 'New State', 'new-state-url');
    }
</script>

Cela ajoute un nouvel état à la pile d'historique en utilisant pushState(). Remarquez ce qu'il ne fait pas : il ne charge pas new-state-url et ne déclenche pas d'événement popstate. pushState met seulement à jour la barre d'adresse et la pile — l'affichage du contenu correspondant est de votre ressort.

Gestion de l'événement Popstate

Lorsque l'utilisateur clique sur les boutons retour ou suivant du navigateur (ou que vous appelez history.back() / history.forward()), l'événement popstate se déclenche. La propriété state de l'événement contient l'objet state que vous avez précédemment passé à pushState/replaceState pour l'entrée maintenant activée. Gérez-le pour restaurer la bonne vue :

window.addEventListener('popstate', function(event) {
        if(event.state) {
            console.log('State changed:', event.state);
            // Handle the state object here
        }
    });

Ce listener réagit aux événements popstate, journalisant les changements et permettant d'ajuster l'état selon l'historique de navigation de l'utilisateur. Lorsque event.state est null, l'utilisateur a navigué en retour vers une entrée créée par un chargement de page normal (qui n'a jamais reçu d'objet state), donc affichez votre vue par défaut.

Remplacement de l'état courant

Parfois, vous souhaitez mettre à jour l'entrée courante sans ajouter un nouvel enregistrement dans la pile — par exemple, synchroniser un filtre ou une sélection d'onglet dans l'URL où l'ajout d'une étape supplémentaire au bouton retour serait gênant. C'est à cela que sert replaceState() :

<div>
    <button onclick="replaceCurrentState()">Replace State</button>
    <p id="replace-status">Ready</p>
</div>

<script>
    function replaceCurrentState() {
        const newState = { id: 'replacedState' };
        // Replace the current state
        window.history.replaceState(newState, 'Replaced State', 'replaced-state-url');
        document.getElementById('replace-status').textContent = 'State replaced successfully!';
    }
</script>

Cela met à jour l'entrée courante en place. Le bouton retour renvoie toujours à la page précédente, car replaceState n'a pas ajouté de nouvelle étape.

Exemple complet de style SPA

Maintenant, rassemblons tout. L'exemple ci-dessous simule une application monopage : cliquer sur un bouton change le contenu d'une div et met à jour l'URL avec pushState, tandis qu'un listener popstate restaure le contenu correct lorsque l'utilisateur navigue avec les boutons retour/suivant. C'est le même schéma qu'un routeur côté client utilise en coulisses.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>SPA Style History API Example</title>
</head>
<body>
    <h1>Page Navigation with History API</h1>
    <div id="content">Start Page</div>

    <!-- Buttons for navigation -->
    <button onclick="loadPage('page1')">Load Page 1</button>
    <button onclick="loadPage('page2')">Load Page 2</button>
    <button onclick="manualGoBack()">Go Back</button>
    <button onclick="manualGoForward()">Go Forward</button>

    <!-- Display the current status of the history -->
    <p id="historyStatus">History Status: Start</p>

    <script>
        // Loads a "page" and updates the browser's history state
        function loadPage(page) {
            const state = { page: page }; // State to be pushed to history
            history.pushState(state, `Page ${page}`, `${page}.html`); // Pushing state to the history
            document.getElementById('content').innerHTML = `<h2>This is ${page.replace('page', 'Page ')}</h2>`; // Update the content
            updateHistoryStatus(state); // Update the history status display
        }

        // Handles the browser's back and forward button actions
        window.addEventListener('popstate', function(event) {
            if (event.state) {
                // Update the page content and history status when navigating through history
                document.getElementById('content').innerHTML = `<h2>This is ${event.state.page.replace('page', 'Page ')}</h2>`;
                updateHistoryStatus(event.state);
            } else {
                // Fallback content when the history does not have any state
                document.getElementById('content').innerHTML = `<h2>Start Page</h2>`;
                document.getElementById('historyStatus').textContent = "History Status: Start";
            }
        });

        // Updates the display of the current history status
        function updateHistoryStatus(state) {
            document.getElementById('historyStatus').textContent = `History Status: ${state.page}`;
        }

        // Function to manually trigger going back in history
        function manualGoBack() {
            history.back();
        }

        // Function to manually trigger going forward in history
        function manualGoForward() {
            history.forward();
        }
    </script>
</body>
</html>

Comment cela fonctionne :

  • Chargement de page dynamiqueloadPage() change le contenu d'une div et appelle pushState pour ajouter une entrée d'historique, de sorte que chaque « page » devient un vrai arrêt retour/suivant.
  • Restauration lors de la navigation — le listener popstate lit event.state.page et restitue le contenu correspondant ; lorsque event.state est null, il revient à la page de départ.
  • Interface familière — les boutons pilotent history.back() et history.forward(), donnant une impression multi-pages sans aucun rechargement complet.

Erreurs courantes

Quelques comportements surprennent régulièrement les développeurs :

  • pushState ne déclenche pas popstate. Seule la navigation utilisateur (retour/suivant, history.go()) le déclenche. Après un pushState, affichez vous-même la nouvelle vue dans la même fonction.
  • L'argument title est ignoré. Presque tous les navigateurs l'ignorent, donc passez "". Pour changer le titre de l'onglet, définissez document.title directement.
  • Les URL doivent avoir la même origine. Passer une url cross-origin génère une SecurityError. Le chemin est résolu relativement au document courant.
  • Le state doit être sérialisable. L'objet state est cloné avec l'algorithme de clonage structuré, donc les fonctions, les nœuds DOM et les instances de classe ne peuvent pas être stockés — et il y a une limite de taille (généralement quelques Mo).
  • Un rechargement de page réexécute votre application à la nouvelle URL. Lorsque l'utilisateur actualise une URL créée par pushState, le serveur doit répondre à ce chemin (ou votre SPA doit le gérer au chargement), sinon il obtient une erreur 404. Ce fallback côté serveur est le problème de routage que les SPA doivent résoudre.

Bonnes pratiques pour l'utilisation de l'API History

  • Gardez le state petit. Stockez un identifiant ou quelques valeurs primitives dans l'objet state et déduisez le reste. N'y versez pas de grands ensembles de données — cela alourdit la session et risque d'atteindre la limite de taille.
  • Mettez toujours à jour document.title vous-même lorsque la « page » change, car l'argument title est ignoré.
  • Gérez la position de défilement. Les navigateurs restaurent le défilement lors d'un popstate via history.scrollRestoration ; définissez-le sur "manual" si vous souhaitez contrôler le défilement vous-même. Consultez les tailles de fenêtre et le défilement pour les API de défilement.
  • Fournissez un fallback côté serveur pour que l'actualisation ou le partage d'une URL créée par pushState fonctionne toujours.
  • Utilisez replaceState pour les mises à jour non navigationnelles (filtres, onglets) afin que le bouton retour reste pertinent.
  • Inspection de l'historique — utilisez history.state pour l'objet state courant et history.length pour le nombre d'entrées dans la pile de session.

Comme l'API History ne fait que modifier l'URL, elle se combine naturellement avec fetch pour charger les données dont chaque « page » a besoin.

Conclusion

L'API History JavaScript vous permet de manipuler l'historique de session du navigateur — en ajoutant, remplaçant et réagissant à la navigation — sans rechargement complet de la page. Maîtriser pushState, replaceState et l'événement popstate, tout en respectant les pièges liés à popstate, à la sérialisation et aux URL de même origine, est ce qui permet aux applications monopages de se sentir aussi rapides et navigables que les sites multi-pages traditionnels.

Pratique

Pratique
Lesquelles des affirmations suivantes correspondent aux fonctionnalités de l'API History JavaScript ?
Lesquelles des affirmations suivantes correspondent aux fonctionnalités de l'API History JavaScript ?
Was this page helpful?