Navigation API

Experimentell: Dies ist eine experimentelle Technologie
Überprüfen Sie die Browser-Kompatibilitätstabelle sorgfältig vor der Verwendung auf produktiven Webseiten.

Die Navigation-API bietet die Möglichkeit, Browser-Navigationsaktionen zu initiieren, abzufangen und zu verwalten. Sie kann auch die Verlaufseinträge einer Anwendung untersuchen. Diese ist ein Nachfolger früherer Web-Plattform-Funktionen wie der History-API und window.location, welche ihre Mängel behebt und speziell auf die Bedürfnisse von Single-Page-Anwendungen (SPAs) zugeschnitten ist.

Konzepte und Verwendung

In SPAs bleibt die Seitenschablone während der Nutzung im Allgemeinen gleich und der Inhalt wird dynamisch neu geschrieben, wenn der Benutzer verschiedene Seiten oder Funktionen besucht. Daher wird im Browser nur eine eindeutige Seite geladen, was die erwartete Benutzererfahrung beim Vor- und Zurücknavigieren zwischen verschiedenen Orten im Verlauf unterbricht. Dieses Problem kann bis zu einem gewissen Grad über die History-API gelöst werden, diese ist jedoch nicht für die Bedürfnisse von SPAs ausgelegt. Die Navigation-API zielt darauf ab, diese Lücke zu schließen.

Auf die API wird über die Window.navigation-Eigenschaft zugegriffen, die eine Referenz auf ein globales Navigation-Objekt zurückgibt. Jedes window-Objekt hat seine eigene entsprechende navigation-Instanz.

Umgang mit Navigationen

Das navigation-Interface hat mehrere zugehörige Events, das bemerkenswerteste ist das navigate-Event. Dieses wird ausgelöst, wenn jede Art von Navigation initiiert wird, was bedeutet, dass Sie alle Seitennavigationen von einem zentralen Ort aus steuern können, ideal für Routing-Funktionen in SPA-Frameworks. (Dies ist nicht der Fall bei der History-API, bei der es manchmal schwierig ist, auf alle Navigationen zu reagieren.) Der navigate-Event-Handler erhält ein NavigateEvent-Objekt, welches detaillierte Informationen enthält, einschließlich Details zu Ziel, Typ, ob es POST-Formulardaten oder eine Download-Anfrage enthält und mehr.

Das NavigationEvent-Objekt stellt auch zwei Methoden bereit:

  • intercept() nimmt als Argument eine Callback-Handler-Funktion entgegen, die ein Promise zurückgibt. Es ermöglicht Ihnen zu steuern, was passiert, wenn die Navigation initiiert wird. Beispielsweise kann es im Fall einer SPA verwendet werden, um relevante neue Inhalte in die Benutzeroberfläche zu laden, basierend auf dem Pfad der URL, zu der navigiert wird.
  • scroll() ermöglicht es Ihnen, das Scroll-Verhalten des Browsers manuell zu initiieren (z. B. zu einem Fragmentbezeichner in der URL), wenn es für Ihren Code sinnvoll ist, anstatt zu warten, bis der Browser es automatisch behandelt.

Sobald eine Navigation initiiert und Ihr intercept()-Handler aufgerufen wird, wird eine Instanz eines NavigationTransition-Objekts erstellt (zugänglich über Navigation.transition), die verwendet werden kann, um den fortlaufenden Navigationsprozess zu verfolgen.

Hinweis: In diesem Zusammenhang bezieht sich "Übergang" auf den Übergang zwischen einem Verlaufseintrag und einem anderen. Es steht nicht im Zusammenhang mit CSS-Übergängen.

Hinweis: Sie können auch preventDefault() aufrufen, um die Navigation vollständig für die meisten Navigationstypen zu stoppen; die Stornierung von Durchsuchen-Navigationen ist noch nicht implementiert.

Wenn das Promise der intercept()-Handler-Funktion erfüllt wird, feuert das Navigation-Objekt das navigatesuccess-Event, das es Ihnen ermöglicht, Bereinigungscode auszuführen, nachdem eine erfolgreiche Navigation abgeschlossen ist. Wenn es abgelehnt wird, d.h. die Navigation fehlgeschlagen ist, wird stattdessen navigateerror ausgelöst, das es Ihnen ermöglicht, den Fehlerfall elegant zu behandeln. Es gibt auch eine finished-Eigenschaft am NavigationTransition-Objekt, welche zur gleichen Zeit erfüllt oder abgelehnt wird, wie die zuvor genannten Events ausgelöst werden. Hierdurch wird ein weiterer Weg zur Behandlung von Erfolgs- und Fehlerfällen bereitgestellt.

Hinweis: Bevor die Navigation-API verfügbar war, hätten Sie, um etwas Ähnliches zu tun, für alle Klick-Events auf Links lauschen, e.preventDefault() ausführen, den passenden History.pushState()-Aufruf ausführen müssen und dann die Seitenansicht basierend auf der neuen URL einrichten müssen. Und dies würde nicht alle Navigationen behandeln – nur benutzerinitiierte Link-Klicks.

Programmatische Aktualisierung und Durchsuchen des Navigation-Verlaufs

Wenn der Benutzer durch Ihre Anwendung navigiert, wird für jeden neuen Ort, zu dem navigiert wird, ein Navigationsverlaufseintrag erstellt. Jeder Verlaufseintrag wird durch eine eindeutige Instanz eines NavigationHistoryEntry-Objekts dargestellt. Diese enthalten mehrere Eigenschaften wie den Schlüssel des Eintrags, die URL und Statusinformationen. Sie können den Eintrag abrufen, auf dem sich der Benutzer gerade befindet, indem Sie Navigation.currentEntry verwenden, und ein Array aller vorhandenen Verlaufseinträge mit Navigation.entries(). Jedes NavigationHistoryEntry-Objekt hat ein dispose-Event, das ausgelöst wird, wenn der Eintrag nicht mehr Teil des Browser-Verlaufs ist. Zum Beispiel, wenn der Benutzer dreimal zurück navigiert und dann zu einem anderen Ort vorwärts navigiert, werden diese drei Verlaufseinträge gelöscht.

Hinweis: Die Navigation-API gibt nur Verlaufseinträge frei, die im aktuellen Browsing-Kontext erstellt wurden und denselben Ursprung wie die aktuelle Seite haben (z. B. keine Navigationen innerhalb eingebetteter <iframe>s oder Cross-Origin-Navigationen). Dadurch wird eine genaue Liste aller vorherigen Verlaufseinträge nur für Ihre App bereitgestellt, was das Durchsuchen des Verlaufs viel weniger fragil macht als mit der älteren History-API.

Das Navigation-Objekt enthält alle Methoden, die Sie benötigen, um den Navigation-Verlauf zu aktualisieren und durch ihn zu durchlaufen:

Navigiert zu einer neuen URL und erstellt einen neuen Navigationsverlaufseintrag.

reload() Experimentell

Lädt den aktuellen Navigationsverlaufseintrag neu.

back() Experimentell

Navigiert zum vorherigen Navigationsverlaufseintrag, falls möglich.

forward() Experimentell

Navigiert zum nächsten Navigationsverlaufseintrag, falls möglich.

traverseTo() Experimentell

Navigiert zu einem bestimmten Navigationsverlaufseintrag, der durch seinen Schlüsselwert identifiziert wird, welcher über die relevante NavigationHistoryEntry.key-Eigenschaft des Eintrags abgerufen wird.

Jede der oben genannten Methoden gibt ein Objekt zurück, das zwei Promises enthält – { committed, finished }. Dies ermöglicht es der aufrufenden Funktion, mit der Ausführung weiterer Aktionen zu warten, bis:

  • committed erfüllt ist, was bedeutet, dass sich die sichtbare URL geändert hat und ein neues NavigationHistoryEntry erstellt wurde.
  • finished erfüllt ist, was bedeutet, dass alle Promises, die von Ihrem intercept()-Handler zurückgegeben werden, erfüllt sind. Dies entspricht dem Erfüllen des NavigationTransition.finished-Promise, wenn das navigatesuccess-Event ausgelöst wird, wie zuvor erwähnt.
  • eines der oben genannten Promises abgelehnt wird, was bedeutet, dass die Navigation aus irgendeinem Grund fehlgeschlagen ist.

Zustand

Die Navigation-API ermöglicht es Ihnen, Zustand auf jedem Verlaufseintrag zu speichern. Dies sind vom Entwickler definierte Informationen – es kann alles sein, was Sie möchten. Beispielsweise möchten Sie möglicherweise eine visitCount-Eigenschaft speichern, die die Anzahl der Besuche einer Ansicht aufzeichnet, oder ein Objekt mit mehreren Eigenschaften im Zusammenhang mit dem UI-Zustand, damit dieser Zustand wiederhergestellt werden kann, wenn ein Benutzer zu dieser Ansicht zurückkehrt.

Um den Zustand eines NavigationHistoryEntry abzurufen, rufen Sie seine getState()-Methode auf. Der Zustand ist anfänglich undefined, aber wenn Zustandsinformationen auf dem Eintrag gesetzt sind, wird er die zuvor festgelegten Zustandsinformationen zurückgeben.

Das Festlegen des Zustands ist etwas nuancierter. Sie können nicht den Zustandswert abrufen und dann direkt aktualisieren – die im Eintrag gespeicherte Kopie wird sich nicht ändern. Stattdessen aktualisieren Sie ihn im Rahmen eines navigate() oder reload() – jede dieser Aktionen nimmt optional ein Objekt mit Optionen entgegen, welches eine state-Eigenschaft enthält, die den neuen Zustand enthält, welcher auf dem Verlaufseintrag festgelegt wird. Wenn diese Navigationen festgeschrieben werden, wird die Zustandsänderung automatisch angewendet.

In einigen Fällen wird jedoch eine Zustandsänderung unabhängig von einer Navigation oder einem Neuladen sein – zum Beispiel, wenn eine Seite ein erweiterbares/zusammenklappbares <details>-Element enthält. In diesem Fall möchten Sie möglicherweise den expandierten/zusammengeklappten Zustand in Ihrem Verlaufseintrag speichern, damit Sie ihn wiederherstellen können, wenn der Benutzer zur Seite zurückkehrt oder seinen Browser neu startet. Solche Fälle werden mit Navigation.updateCurrentEntry() behandelt. Das currententrychange-Event wird ausgelöst, wenn die aktuelle Eintragsänderung abgeschlossen ist.

Einschränkungen

Es gibt ein paar wahrgenommene Einschränkungen mit der Navigation-API:

  1. Die aktuelle Spezifikation löst kein navigate-Event beim ersten Laden einer Seite aus. Das könnte für Websites in Ordnung sein, die Server-Side-Rendering (SSR) verwenden – Ihr Server könnte den korrekten Anfangszustand zurückgeben, was der schnellste Weg ist, um Inhalte zu Ihren Benutzern zu bringen. Aber Websites, die Client-seitigen Code verwenden, um ihre Seiten zu erstellen, benötigen möglicherweise eine zusätzliche Funktion zur Initialisierung der Seite.
  2. Die Navigation-API operiert nur innerhalb eines einzelnen Frames – der obersten Seite oder eines spezifischen <iframe>. Dies hat einige interessante Implikationen, die weiter in der Spezifikation dokumentiert sind, aber in der Praxis die Verwirrung der Entwickler reduzieren werden. Die vorherige History-API hat mehrere verwirrende Randfälle, wie z. B. Unterstützung für Frames, die die Navigation-API von vornherein behandelt.
  3. Sie können momentan die Navigation-API nicht verwenden, um die Historienliste programmatisch zu verändern oder neu zu arrangieren. Es könnte nützlich sein, eine temporäre Anzeige zu haben, z. B. den Benutzer zu einem temporären Modal zu navigieren, das ihn nach einigen Informationen fragt, und dann zur vorherigen URL zurückzukehren. In diesem Fall würden Sie den temporären Modal-Verlaufseintrag löschen wollen, damit der Benutzer den Anwendungsfluss nicht durcheinander bringt, indem er die Vorwärtstaste drückt und ihn erneut öffnet.

Schnittstellen

Ereignisobjekt für das navigate-Event, welches ausgelöst wird, wenn jede Art von Navigation initiiert wird. Es bietet Zugriff auf Informationen zu dieser Navigation und am bemerkenswertesten die intercept(), welche ermöglicht zu kontrollieren, was passiert, wenn die Navigation initiiert wird.

Ermöglicht die Kontrolle über alle Navigationsaktionen für das aktuelle window an einem zentralen Ort, einschließlich der programmgesteuerten Initiierung von Navigationen, der Untersuchung von Navigationsverlaufseinträgen und der Verwaltung von Navigationen, während sie geschehen.

Repräsentiert eine kürzlich erfolgte cross-document Navigation. Sie enthält den Navigationstyp und aktuelle sowie Ziel-Dokumentverlaufseinträge.

Ereignisobjekt für das currententrychange-Event, welches feuert, wenn sich der Navigation.currentEntry geändert hat. Es bietet Zugriff auf den Navigationstyp und den vorherigen Verlaufseintrag, von dem aus navigiert wurde.

Repräsentiert das Ziel, zu dem in der aktuellen Navigation navigiert wird.

Repräsentiert einen einzelnen Navigationsverlaufseintrag.

Repräsentiert eine laufende Navigation.

Erweiterungen für andere Schnittstellen

Window.navigation Schreibgeschützt Experimentell

Gibt das aktuelle window-assoziierte Navigation-Objekt zurück. Dies ist der Einstiegspunkt für die Navigation-API.

Beispiele

Hinweis: Schauen Sie sich das Navigation API live demo (Demoquellcode anzeigen) an.

Verwaltung einer Navigation mit intercept()

js
navigation.addEventListener("navigate", (event) => {
  // Exit early if this navigation shouldn't be intercepted,
  // e.g. if the navigation is cross-origin, or a download request
  if (shouldNotIntercept(event)) {
    return;
  }

  const url = new URL(event.destination.url);

  if (url.pathname.startsWith("/articles/")) {
    event.intercept({
      async handler() {
        // The URL has already changed, so show a placeholder while
        // fetching the new content, such as a spinner or loading page
        renderArticlePagePlaceholder();

        // Fetch the new content and display when ready
        const articleContent = await getArticleContent(url.pathname);
        renderArticlePage(articleContent);
      },
    });
  }
});

Verwaltung des Scrollens mit scroll()

In diesem Beispiel zum Abfangen einer Navigation beginnt die Funktion handler() damit, einige Artikelinhalte abzurufen und darzustellen, und lädt dann einige sekundäre Inhalte danach. Es macht Sinn, die Seite zum Hauptartikelinhalt zu scrollen, sobald er verfügbar ist, damit der Benutzer damit interagieren kann, anstatt darauf zu warten, bis auch der sekundäre Inhalt dargestellt ist. Um dies zu erreichen, haben wir zwischen den beiden einen scroll()-Aufruf hinzugefügt.

js
navigation.addEventListener("navigate", (event) => {
  if (shouldNotIntercept(event)) {
    return;
  }
  const url = new URL(event.destination.url);

  if (url.pathname.startsWith("/articles/")) {
    event.intercept({
      async handler() {
        const articleContent = await getArticleContent(url.pathname);
        renderArticlePage(articleContent);

        event.scroll();

        const secondaryContent = await getSecondaryContent(url.pathname);
        addSecondaryContent(secondaryContent);
      },
    });
  }
});

Durchlaufen zu einem bestimmten Verlaufseintrag

js
// On JS startup, get the key of the first loaded page
// so the user can always go back there.
const { key } = navigation.currentEntry;
backToHomeButton.onclick = () => navigation.traverseTo(key);

// Navigate away, but the button will always work.
await navigation.navigate("/another_url").finished;

Aktualisieren des Zustands

js
navigation.navigate(url, { state: newState });

Oder

js
navigation.reload({ state: newState });

Oder wenn der Zustand unabhängig von einer Navigation oder einem Neuladen ist:

js
navigation.updateCurrentEntry({ state: newState });

Spezifikationen

Specification
HTML
# navigation-api

Browser-Kompatibilität

api.Navigation

api.NavigationDestination

api.NavigationHistoryEntry

api.NavigationTransition

Siehe auch