Start · Sprachen · JavaScript · Referenz · History.pushState()

History.pushState()

Methode

Fügt dem Sitzungsverlauf des Browsers einen neuen Eintrag hinzu und ändert die angezeigte URL, ohne die Seite neu zu laden.

seit JavaScript Baseline: alle Browser (HTML5) Kategorie: webapi

Signatur

History.prototype.pushState(state, unused, url)

Beschreibung

history.pushState() legt einen neuen Eintrag auf den Verlaufsstapel der aktuellen Sitzung. Die Adressleiste zeigt danach die neue URL an, der Browser lädt aber nichts nach und prüft auch nicht, ob die URL auf dem Server existiert. Das ist die Grundlage für clientseitiges Routing in Single-Page-Applications (SPA): Die Ansicht wechselt per JavaScript, die URL bleibt trotzdem teilbar und als Lesezeichen nutzbar.

Über den Parameter state kann man jedem Eintrag ein serialisierbares Objekt mitgeben. Wenn der Nutzer später mit Zurück/Vor navigiert, löst der Browser das Ereignis popstate aus. Dessen Eigenschaft event.state enthält eine Kopie dieses Objekts, mit der die Anwendung die passende Ansicht wiederherstellt. Den Zustand des aktuellen Eintrags liest man jederzeit über history.state.

Im Unterschied zu history.replaceState(), das den aktuellen Eintrag überschreibt, erzeugt pushState() immer einen zusätzlichen Eintrag. Alle „Vorwärts“-Einträge hinter der aktuellen Position werden dabei verworfen. Verwende pushState() für echte Navigationsschritte, etwa beim Wechsel zu einer anderen Seite der App. Für kleine Zustandsänderungen wie Filter oder Scroll-Position ist replaceState() meist besser, damit der Verlauf nicht mit Einträgen zugemüllt wird.

Parameter

Name Typ Default Beschreibung
$state Pflicht any (strukturiert klonbar) | null Beliebiges Objekt, das dem neuen Eintrag zugeordnet wird. Es wird mit dem Structured-Clone-Algorithmus gespeichert, deshalb sind Funktionen, DOM-Knoten und Ähnliches nicht erlaubt. Wenn du keinen Zustand brauchst, übergib null.
$unused Pflicht string "" Früher als Titel gedacht, wird aber von allen Browsern ignoriert. Aus Kompatibilitätsgründen trotzdem angeben, üblich ist ein leerer String "".
$url string | URL aktuelle URL Neue URL des Eintrags, relativ oder absolut. Sie muss dieselbe Origin haben wie das aktuelle Dokument, sonst wird ein SecurityError geworfen. Wenn du sie weglässt, bleibt die aktuelle URL erhalten.

Rückgabewert

Typ
void
Beschreibung
Gibt undefined zurück.

Beispiele

Einfacher Verlaufseintrag mit Zustand

// Aktuelle Seite: https://example.com/
history.pushState({ page: 2 }, "", "/produkte?seite=2");

console.log(location.pathname + location.search);
console.log(history.state);
/produkte?seite=2 { page: 2 }

Minimaler SPA-Router mit popstate

const app = document.querySelector("#app");

const render = (route) => {
  app.textContent = `Ansicht: ${route}`;
};

// Interne Links abfangen und per pushState navigieren
document.addEventListener("click", (event) => {
  const link = event.target.closest("a[data-route]");
  if (!link) return;
  event.preventDefault();
  const route = link.dataset.route;
  history.pushState({ route }, "", link.href);
  render(route); // pushState löst KEIN popstate aus!
});

// Zurück/Vor-Buttons des Browsers behandeln
window.addEventListener("popstate", (event) => {
  render(event.state?.route ?? "start");
});

// Startzustand für den ersten Eintrag setzen
history.replaceState({ route: "start" }, "", location.href);
render("start");

Zustand eines Detail-Dialogs in der URL festhalten

const openProduct = async (id) => {
  const res = await fetch(`/api/produkte/${id}`);
  const product = await res.json();
  showDialog(product);
  history.pushState({ dialog: "produkt", id }, "", `/produkte/${id}`);
};

const showDialog = (product) => {
  console.log(`Dialog geöffnet: ${product.name}`);
};

window.addEventListener("popstate", (event) => {
  if (event.state?.dialog !== "produkt") {
    console.log("Dialog geschlossen");
  }
});
Dialog geöffnet: Laptop

SecurityError bei fremder Origin abfangen

try {
  history.pushState(null, "", "https://andere-domain.de/seite");
} catch (err) {
  console.log(err.name);
}
SecurityError

// Wichtig · Fallstricke

  • Kein popstate beim Aufruf: pushState() selbst löst popstate nicht aus. Die Ansicht musst du direkt nach dem Aufruf selbst aktualisieren. popstate kommt nur bei Zurück/Vor, bei history.back()/go() und Ähnlichem.
  • Server-Konfiguration: Lädt der Nutzer eine per pushState() gesetzte URL neu, fragt der Browser genau diese URL beim Server an. Der Server muss dann die App ausliefern (Fallback auf index.html), sonst gibt es einen 404-Fehler.
  • Same-Origin-Pflicht: Eine URL mit anderem Protokoll, anderem Host oder anderem Port wirft einen SecurityError. Bei file://-URLs verhalten sich die Browser unterschiedlich restriktiv.
  • Größe des Zustands: Das state-Objekt wird serialisiert und kann auf der Festplatte gespeichert werden. Firefox begrenzt es auf etwa 16 MiB. Speichere dort nur IDs oder kleine Daten, keine großen Datenmengen und keine sensiblen Daten wie Tokens.
  • Nicht klonbare Werte wie Funktionen oder DOM-Elemente führen zu einem DataCloneError.
  • Drosselung: Browser begrenzen sehr häufige Aufrufe, Safari und Chrome z. B. auf ca. 100 Aufrufe in 10 Sekunden. Für Scroll- oder Eingabe-Ereignisse besser gedrosselt replaceState() verwenden.
  • Nur ein Wechsel des Fragments (#abc) per pushState() löst kein hashchange aus.
  • Sicherheit: Werte aus event.state oder location niemals ungeprüft per innerHTML ausgeben, denn URLs sind vom Nutzer manipulierbar (XSS-Gefahr).
  • Wird von allen modernen Browsern unterstützt. Für neue Projekte in Chromium-Browsern lohnt ein Blick auf die modernere Navigation API.