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) :
| Membre | Ce 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.state | Copie en lecture seule de l'objet state de l'entrée courante. |
history.length | Nombre 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
Navigation entre les états
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 dynamique —
loadPage()change le contenu d'une div et appellepushStatepour 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
popstatelitevent.state.pageet restitue le contenu correspondant ; lorsqueevent.stateestnull, il revient à la page de départ. - Interface familière — les boutons pilotent
history.back()ethistory.forward(), donnant une impression multi-pages sans aucun rechargement complet.
Erreurs courantes
Quelques comportements surprennent régulièrement les développeurs :
pushStatene déclenche paspopstate. Seule la navigation utilisateur (retour/suivant,history.go()) le déclenche. Après unpushState, affichez vous-même la nouvelle vue dans la même fonction.- L'argument
titleest ignoré. Presque tous les navigateurs l'ignorent, donc passez"". Pour changer le titre de l'onglet, définissezdocument.titledirectement. - Les URL doivent avoir la même origine. Passer une
urlcross-origin génère uneSecurityError. 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.titlevous-même lorsque la « page » change, car l'argumenttitleest ignoré. - Gérez la position de défilement. Les navigateurs restaurent le défilement lors d'un
popstateviahistory.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
pushStatefonctionne toujours. - Utilisez
replaceStatepour les mises à jour non navigationnelles (filtres, onglets) afin que le bouton retour reste pertinent. - Inspection de l'historique — utilisez
history.statepour l'objet state courant ethistory.lengthpour 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.