JavaScript History API
Erfahren Sie, wie die JavaScript History API die Browser-History manipuliert und SPAs ohne Seitenneuladen ermöglicht.
Einführung in die JavaScript History API
In der modernen Webentwicklung erfordert das Schaffen nahtloser Benutzererfahrungen häufig das Manipulieren der Browser-History. Die JavaScript History API ermöglicht es Ihnen, die Sitzungshistorie des Browsers zu lesen und zu ändern — also die Liste der Seiten (und URL-Zustände), die der Benutzer im aktuellen Tab besucht hat. Mit dieser API können Entwickler die Adressleiste und den Vor-/Zurück-Stapel aktualisieren, ohne einen vollständigen Seitenneuladevorgang auszulösen — genau das, was Single-Page-Anwendungen (SPAs) benötigen, um schnell und nativ zu wirken.
Diese Seite behandelt die drei Kernmethoden (pushState, replaceState und die Navigations-Hilfsmethoden), wie man auf das popstate-Ereignis reagiert, typische Fallstricke sowie Best Practices für den produktiven Einsatz der API.
Warum die History API existiert
Vor dieser API war die einzige Möglichkeit, die URL zu ändern, die Navigation, die das gesamte Dokument neu lud. SPAs rendern neue „Seiten" mit JavaScript, daher muss die URL synchron bleiben, ohne Neuladen — andernfalls sind der Zurück-Button, Lesezeichen und teilbare Links funktionslos.
Die History API löst dieses Problem, indem sie Ihnen folgende Möglichkeiten bietet:
- Hinzufügen eines neuen Eintrags zum Vor-/Zurück-Stapel (
pushState). - Ändern des aktuellen Eintrags an Ort und Stelle (
replaceState). - Reagieren wenn der Benutzer sich mit den Vor-/Zurück-Tasten durch den Stapel bewegt (
popstate).
Den Datenzustand des aktuellen Eintrags lesen Sie über die schreibgeschützte Eigenschaft history.state, und history.length gibt an, wie viele Einträge sich im Sitzungsstapel befinden. Zum Aufbau der URLs, die Sie an diese Methoden übergeben, ist das URL-Objekt ein nützlicher Begleiter.
Das history-Objekt auf einen Blick
Die API wird über das globale window.history-Objekt bereitgestellt (Sie können auch einfach history schreiben):
| Member | Funktion |
|---|---|
history.pushState(state, title, url) | Fügt einen neuen Eintrag zum History-Stapel hinzu und aktualisiert die URL. |
history.replaceState(state, title, url) | Ersetzt den aktuellen Eintrag — es wird kein neuer Stapeleintrag angelegt. |
history.state | Schreibgeschützte Kopie des state-Objekts des aktuellen Eintrags. |
history.length | Anzahl der Einträge in der Sitzungshistorie. |
history.back() | Geht einen Eintrag zurück (entspricht dem Browser-Zurück-Button). |
history.forward() | Geht einen Eintrag vor. |
history.go(n) | Springt n Einträge (negativ = zurück, positiv = vorwärts). |
Verwendung der History API in Webanwendungen
Navigation zwischen Zuständen
Die History API ermöglicht die Navigation zwischen verschiedenen Zuständen einer Anwendung, ohne die Seite neu zu laden. pushState() nimmt drei Argumente entgegen — ein state-object (ein beliebiger serialisierbarer Wert), einen title (von den meisten Browsern ignoriert, übergeben Sie daher einen leeren string) und eine url (relativ zur aktuellen Seite aufgelöst und muss same-origin sein). So fügen Sie einen neuen Zustand hinzu:
<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>Damit wird ein neuer Zustand mit pushState() zum History-Stapel hinzugefügt. Beachten Sie, was dabei nicht passiert: new-state-url wird nicht geladen, und kein popstate-Ereignis wird ausgelöst. pushState aktualisiert nur die Adressleiste und den Stapel — das Rendern des passenden Inhalts ist Ihre Aufgabe.
Umgang mit dem Popstate-Ereignis
Wenn der Benutzer auf den Zurück- oder Vorwärts-Button des Browsers klickt (oder Sie history.back() / history.forward() aufrufen), wird das popstate-Ereignis ausgelöst. Die state-Eigenschaft des Ereignisses enthält das state-object, das Sie zuvor für den nun aktivierten Eintrag an pushState/replaceState übergeben haben. Reagieren Sie darauf, um die richtige Ansicht wiederherzustellen:
window.addEventListener('popstate', function(event) {
if(event.state) {
console.log('State changed:', event.state);
// Handle the state object here
}
});Dieser Listener reagiert auf popstate-Ereignisse, protokolliert Änderungen und ermöglicht die Anpassung des Zustands anhand der Navigationshistorie des Benutzers. Wenn event.state null ist, hat der Benutzer zu einem Eintrag zurücknavigiert, der durch einen normalen Seitenaufruf erstellt wurde (dem nie ein state-object übergeben wurde) — zeigen Sie dann Ihre Standardansicht an.
Den aktuellen Zustand ersetzen
Manchmal möchten Sie den aktuellen Eintrag aktualisieren, ohne einen neuen Datensatz im Stapel anzulegen — etwa beim Synchronisieren eines Filters oder einer Tab-Auswahl in die URL, wo ein zusätzlicher Zurück-Schritt störend wäre. Dafür ist replaceState() gedacht:
<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>Damit wird der aktuelle Eintrag an Ort und Stelle aktualisiert. Der Zurück-Button führt weiterhin zu der Seite, die zuvor aufgerufen wurde, da replaceState keinen neuen Schritt hinzufügt.
Vollständiges SPA-Beispiel
Bringen wir nun alles zusammen. Das folgende Beispiel simuliert eine Single-Page-Anwendung: Ein Button-Klick tauscht den Inhalt eines div aus und aktualisiert die URL mit pushState, während ein popstate-Listener den richtigen Inhalt wiederherstellt, wenn der Benutzer mit den Vor-/Zurück-Buttons navigiert. Dies ist dasselbe Muster, das ein clientseitiger Router intern verwendet.
<!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>So funktioniert es:
- Dynamisches Laden von Seiten —
loadPage()ändert den Inhalt eines div und ruftpushStateauf, um einen History-Eintrag hinzuzufügen, sodass jede „Seite" ein echter Vor-/Zurück-Stopp wird. - Wiederherstellung bei Navigation — der
popstate-Listener liestevent.state.pageund rendert den passenden Inhalt neu; wennevent.statenullist, wird auf die Startseite zurückgefallen. - Vertraute Benutzeroberfläche — die Buttons steuern
history.back()undhistory.forward(), was ein mehrseitiges Feeling ohne einen einzigen vollständigen Neuladevorgang erzeugt.
Typische Fallstricke
Einige Verhaltensweisen überraschen Entwickler regelmäßig:
pushStatelöst keinpopstateaus. Nur Benutzernavigation (Vor-/Zurück,history.go()) löst es aus. Nach einempushStaterendern Sie die neue Ansicht selbst in derselben Funktion.- Das
title-Argument wird ignoriert. Fast jeder Browser ignoriert es, übergeben Sie daher"". Um den Tab-Titel zu ändern, setzen Siedocument.titledirekt. - URLs müssen same-origin sein. Das Übergeben einer cross-origin
urlwirft einenSecurityError. Der Pfad wird relativ zum aktuellen Dokument aufgelöst. - Der Zustand muss serialisierbar sein. Das state-object wird mit dem Structured-Clone-Algorithmus geklont, daher können Funktionen, DOM-Knoten und Klasseninstanzen nicht gespeichert werden — und es gibt ein Größenlimit (üblicherweise einige MB).
- Ein Seitenneuladung startet Ihre App unter der neuen URL neu. Wenn der Benutzer eine per
pushStategesetzte URL aktualisiert, muss der Server auf diesen Pfad antworten (oder Ihre SPA muss ihn beim Laden verarbeiten), sonst erhalten sie eine 404. Diese serverseitige Fallback-Lösung ist das Routing-Problem, das SPAs lösen müssen.
Best Practices für die Verwendung der History API
- Halten Sie den Zustand klein. Speichern Sie einen Bezeichner oder wenige primitive Werte im state-object und leiten Sie den Rest ab. Keine großen Datensätze in die History dumpen — das bläht die Sitzung auf und riskiert das Überschreiten des Größenlimits.
- Aktualisieren Sie
document.titlestets selbst, wenn die „Seite" wechselt, da dastitle-Argument ignoriert wird. - Scroll-Position verwalten. Browser stellen den Scroll-Zustand bei
popstateüberhistory.scrollRestorationwieder her; setzen Sie es auf"manual", wenn Sie das Scrollen selbst steuern möchten. Weitere Scroll-APIs finden Sie unter Fenstergrößen und Scrollen. - Stellen Sie einen serverseitigen Fallback bereit, damit das Aktualisieren oder Teilen einer per
pushStategesetzten URL weiterhin funktioniert. - Verwenden Sie
replaceStatefür nicht-navigatorische Aktualisierungen (Filter, Tabs), damit der Zurück-Button sinnvoll bleibt. - History prüfen — nutzen Sie
history.statefür das aktuelle state-object undhistory.lengthfür die Anzahl der Einträge im Sitzungsstapel.
Da die History API nur die URL ändert, ergänzt sie sich natürlich mit fetch zum Laden der Daten, die jede „Seite" benötigt.
Fazit
Die JavaScript History API ermöglicht es Ihnen, die Sitzungshistorie des Browsers zu manipulieren — Einträge hinzuzufügen, zu ersetzen und auf Navigation zu reagieren — ohne vollständige Seitenneuladezyklen. Das Beherrschen von pushState, replaceState und dem popstate-Ereignis sowie das Kennen der Fallstricke rund um popstate, Serialisierung und same-origin-URLs ist es, was Single-Page-Anwendungen so schnell und navigierbar erscheinen lässt wie traditionelle mehrseitige Websites.