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 Einträge der Historie einer Anwendung prüfen. Dies ist ein Nachfolger früherer Webplattform-Funktionen wie der History API und window.location
, die deren Schwächen beheben und speziell auf die Bedürfnisse von Single-Page-Applications (SPAs) zugeschnitten sind.
Konzepte und Nutzung
In SPAs bleibt die Seitenvorlage während der Nutzung meist gleich, und die Inhalte werden dynamisch neu geschrieben, wenn der Benutzer verschiedene Seiten oder Funktionen besucht. Dadurch wird nur eine einzige eindeutige Seite im Browser geladen, was das erwartete Benutzererlebnis von Vorwärts- und Rückwärtsnavigation zwischen verschiedenen Orten im Anzeigeverlauf beeinträchtigt. Dieses Problem kann bis zu einem gewissen Grad über die History API gelöst werden, aber sie ist 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 Navigationsaktionen
Das navigation
-Interface verfügt über mehrere zugehörige Ereignisse, am bemerkenswertesten ist das navigate
-Ereignis. Dies wird ausgelöst, wenn jede Art von Navigation initiiert wird, was bedeutet, dass Sie alle Seiten-Navigationsaktionen von einem zentralen Ort aus steuern können, ideal für Routing-Funktionen in SPA-Frameworks. (Bei der History API ist dies nicht der Fall, bei der es manchmal schwierig ist, zu reagieren auf alle Navigationsaktionen.) Der navigate
-Ereignishandler erhält ein NavigateEvent
-Objekt, das detaillierte Informationen einschließlich Details zum Ziel der Navigation, Typ, ob es POST
-Formulardaten oder eine Download-Anfrage enthält und mehr bereitstellt.
Das NavigationEvent
-Objekt bietet außerdem zwei Methoden:
intercept()
nimmt als Argument eine Callback-Handler-Funktion entgegen, die ein Versprechen zurückgibt. Es ermöglicht Ihnen zu kontrollieren, was passiert, wenn die Navigation initiiert wird. Zum Beispiel kann es im Fall einer SPA verwendet werden, um relevanten neuen Inhalt in die Oberfläche zu laden, basierend auf dem Pfad der URL, zu der navigiert wurde.scroll()
ermöglicht es Ihnen, das Scrollverhalten des Browsers manuell zu initiieren (z. B. zu einem Fragment-Identifikator in der URL), wenn es für Ihren Code sinnvoll ist, anstatt darauf zu warten, dass der Browser es automatisch handhabt.
Sobald eine Navigation initiiert wird und Ihr intercept()
-Handler aufgerufen wird, wird ein NavigationTransition
-Objekt instanziiert (zugänglich über Navigation.transition
), das verwendet werden kann, um den Prozess der laufenden Navigation zu verfolgen.
Hinweis: In diesem Kontext bezieht sich "transition" auf den Übergang zwischen einem Eintrag der Historie und einem anderen. Es bezieht sich nicht auf CSS-Übergänge.
Hinweis:
Sie können auch preventDefault()
aufrufen, um die Navigation für die meisten Navigationsarten vollständig zu stoppen; das Stornieren von Traverse-Navigationen ist noch nicht implementiert.
Wenn das Versprechen der intercept()
-Handler-Funktion erfüllt wird, wird das navigatesuccess
-Ereignis des Navigation
-Objekts ausgelöst, sodass Sie nach Abschluss einer erfolgreichen Navigation Aufräumcode ausführen können. Wenn es abgelehnt wird, bedeutet dies, dass die Navigation fehlgeschlagen ist, und navigateerror
wird stattdessen ausgelöst, sodass Sie den Fehlerfall elegant behandeln können. Es gibt auch eine finished
-Eigenschaft im NavigationTransition
-Objekt, das zur gleichen Zeit wie die oben genannten Ereignisse erfüllt oder abgelehnt wird, was einen weiteren Weg zur Behandlung der Erfolgs- und Fehlerfälle bietet.
Hinweis:
Bevor die Navigation API verfügbar war, um etwas Ähnliches zu tun, hätten Sie auf alle Klick-Ereignisse auf Links lauschen, e.preventDefault()
ausführen, den entsprechenden History.pushState()
Aufruf durchführen und dann die Seitenansicht basierend auf der neuen URL einrichten müssen. Und das würde nicht alle Navigationsarten abdecken, sondern nur vom Benutzer initiierte Link-Klicks.
Programmgesteuertes Aktualisieren und Durchlaufen der Navigationshistorie
Während der Benutzer durch Ihre Anwendung navigiert, führt jeder neue Standort, zu dem navigiert wird, zur Erstellung eines Eintrags der Navigationshistorie. Jeder Historiensatz wird durch eine eindeutige NavigationHistoryEntry
-Objektinstanz dargestellt. Diese enthalten mehrere Eigenschaften wie den Schlüssel des Eintrags, die URL und Statusinformationen. Sie können den Eintrag, auf dem sich der Benutzer gerade befindet, mit Navigation.currentEntry
abrufen und ein Array aller vorhandenen Historieeinträge mit Navigation.entries()
. Jedes NavigationHistoryEntry
-Objekt hat ein dispose
-Ereignis, das ausgelöst wird, wenn der Eintrag nicht mehr Teil der Browser-Historie ist. Zum Beispiel, wenn der Benutzer dreimal zurück navigiert und dann zu einem anderen Ort vorwärts navigiert, werden diese drei Historieeinträge entsorgt.
Hinweis:
Die Navigation API gibt nur die Historieeinträge im aktuellen Browsing-Kontext preis, die den gleichen Ursprung wie die aktuelle Seite haben (z. B. keine Navigationsaktionen innerhalb eingebetteter <iframe>
s oder Ursprungsübergreifende Navigationsaktionen), was eine genaue Liste aller vorherigen Historieeinträge nur für Ihre App liefert. Dies macht das Durchlaufen der Historie zu einer viel weniger anfälligen Angelegenheit als mit der älteren History API.
Das Navigation
-Objekt enthält alle Methoden, die Sie benötigen, um die Navigationshistorie zu aktualisieren und durch sie zu navigieren:
-
Navigiert zu einer neuen URL und erstellt einen neuen Eintrag in der Navigationshistorie.
reload()
Experimentell-
Lädt den aktuellen Eintrag der Navigationshistorie neu.
back()
Experimentell-
Navigiert zum vorherigen Eintrag der Navigationshistorie, falls dies möglich ist.
forward()
Experimentell-
Navigiert zum nächsten Eintrag der Navigationshistorie, falls dies möglich ist.
traverseTo()
Experimentell-
Navigiert zu einem bestimmten Eintrag der Navigationshistorie, der durch seinen Schlüsselwert identifiziert wird, der über die relevante
NavigationHistoryEntry.key
-Eigenschaft des Eintrags erhalten wird.
Jede der oben genannten Methoden gibt ein Objekt zurück, das zwei Versprechen enthält — { committed, finished }
. Auf diese Weise kann die aufrufende Funktion warten, bevor sie weitere Aktionen ergreift, bis:
committed
erfüllt ist, was bedeutet, dass sich die sichtbare URL geändert hat und ein neuerNavigationHistoryEntry
erstellt wurde.finished
erfüllt ist, was bedeutet, dass alle vomintercept()
-Handler zurückgegebenen Versprechen erfüllt sind. Dies entspricht dem Erfüllen desNavigationTransition.finished
-Versprechens, wenn dasnavigatesuccess
-Ereignis ausgelöst wird, wie oben erwähnt.- eines der oben genannten Versprechen abgelehnt wird, was bedeutet, dass die Navigation aus irgendeinem Grund fehlgeschlagen ist.
Zustand
Die Navigation API ermöglicht es Ihnen, einen Zustand auf jedem Eintrag der Historie zu speichern. Dies sind vom Entwickler definierte Informationen — es kann alles sein, was Sie möchten. Zum Beispiel möchten Sie möglicherweise eine visitCount
-Eigenschaft speichern, die die Anzahl der Besuche eines View zählt, oder ein Objekt, das mehrere Eigenschaften in Bezug auf den UI-Zustand enthält, sodass dieser Zustand wiederhergestellt werden kann, wenn ein Benutzer zu diesem View zurückkehrt.
Um den Zustand eines NavigationHistoryEntry
zu erhalten, rufen Sie dessen getState()
-Methode auf. Es ist zunächst undefined
, aber wenn Zustand-Informationen auf dem Eintrag gesetzt werden, wird es die zuvor gesetzten Zustand-Informationen zurückgeben.
Das Setzen des Zustands ist etwas nuancierter. Sie können den Zustandswert nicht abrufen und dann direkt aktualisieren — die Kopie, die auf dem Eintrag gespeichert ist, wird sich nicht ändern. Stattdessen aktualisieren Sie es, während Sie ein navigate()
oder reload()
durchführen — jede dieser Methoden nimmt optional ein options-Objekt-Parameter, das eine state
-Eigenschaft enthält, die den neuen Zustand enthält, der auf dem Historieeintrag gesetzt werden soll. Wenn diese Navigationsaktionen bestätigt werden, wird die Zustandsänderung automatisch angewendet.
In einigen Fällen wird eine Zustandsänderung jedoch 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 erweiterten/zusammengeklappten Zustand in Ihrem Historieeintrag speichern, damit Sie ihn wiederherstellen können, wenn der Benutzer zur Seite zurückkehrt oder seinen Browser neu startet. Fälle wie dieser werden mit Navigation.updateCurrentEntry()
behandelt. Das currententrychange
-Ereignis wird ausgelöst, wenn die aktuelle Eintragsänderung abgeschlossen ist.
Einschränkungen
Es gibt ein paar wahrgenommene Einschränkungen bei der Navigation API:
- Die aktuelle Spezifikation löst kein
navigate
-Ereignis beim ersten Laden einer Seite aus. Dies könnte für Websites, die Server Side Rendering (SSR) verwenden, in Ordnung sein — Ihr Server könnte den richtigen Anfangszustand zurückgeben, was der schnellste Weg ist, um Inhalt an Ihre Benutzer zu liefern. Aber Websites, die clientseitigen Code verwenden, um ihre Seiten zu erstellen, benötigen möglicherweise eine zusätzliche Funktion, um die Seite zu initialisieren. - Die Navigation API operiert nur innerhalb eines einzelnen Rahmens — der obersten Seite oder eines bestimmten
<iframe>
. Dies hat einige interessante Implikationen, die weiter in der Spezifikation dokumentiert sind, aber in der Praxis wird dies Verwirrung bei Entwicklern reduzieren. Die vorherige History API hatte mehrere verwirrende Randfälle, wie die Unterstützung für Frames, die die Navigation API im Voraus behandelt. - Derzeit können Sie die Navigation API nicht verwenden, um die Liste der Historieeinträge programmgesteuert zu ändern oder neu anzuordnen. Es könnte zum Beispiel nützlich sein, einen temporären Zustand zu haben, indem Sie den Benutzer zu einem temporären Modal weiterleiten, das ihn nach einigen Informationen fragt, und dann zur vorherigen URL zurückkehren. In diesem Fall möchten Sie den temporären Modal-Navigations-Eintrag löschen, damit der Benutzer den Anwendungsfluss nicht durch Drücken der Vorwärts-Taste stören und ihn erneut öffnen kann.
Schnittstellen
-
Ereignisobjekt für das
navigate
-Ereignis, das ausgelöst wird, wenn jeder Navigationsart initiiert wird. Es bietet Zugriff auf Informationen über diese Navigation und insbesondere dieintercept()
, die es Ihnen ermöglicht, zu kontrollieren, was passiert, wenn die Navigation initiiert wird. -
Erlaubt die Kontrolle über alle Navigationsaktionen für das aktuelle
window
an einem zentralen Ort, einschließlich der programmatischen Initiierung von Navigationsaktionen, der Prüfung von Historieeinträgen und der Verwaltung von Navigationsaktionen während ihres Ablaufs. -
Repräsentiert eine kürzlich durchgeführte dokumentübergreifende Navigation. Sie enthält den Navigationstyp und die aktuellen und Zieldokument-Historieeinträge.
-
Ereignisobjekt für das
currententrychange
-Ereignis, das auftritt, wenn sichNavigation.currentEntry
geändert hat. Es bietet Zugriff auf den Navigationstyp und den vorherigen Historieeintrag, von dem aus navigiert wurde. -
Repräsentiert das Ziel, zu dem in der aktuellen Navigation navigiert wird.
-
Repräsentiert einen einzelnen Historieeintrag.
-
Repräsentiert eine laufende Navigation.
Erweiterungen zu anderen Schnittstellen
-
Gibt das zum aktuellen
window
gehörendeNavigation
-Objekt zurück. Dies ist der Einstiegspunkt für die Navigation API.
Beispiele
Hinweis: Sehen Sie sich Domenic Denicolas Navigation API Live-Demo an.
Umgang mit einer Navigation mittels intercept()
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);
},
});
}
});
Umgang mit dem Scrollen mittels scroll()
In diesem Beispiel des Abfangens einer Navigation beginnt die handler()
-Funktion mit dem Abrufen und Rendern von Artikelinhalten, aber dann werden einige sekundäre Inhalte danach abgerufen und gerendert. Es macht Sinn, die Seite so schnell wie möglich zu den Hauptartikelinhalten zu scrollen, damit der Benutzer damit interagieren kann, anstatt zu warten, bis auch die sekundären Inhalte gerendert sind. Um dies zu erreichen, haben wir einen scroll()
-Aufruf zwischen die beiden hinzugefügt.
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 Historieeintrag
// 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
navigation.navigate(url, { state: newState });
Oder
navigation.reload({ state: newState });
Oder falls der Zustand unabhängig von einer Navigation oder einem Neuladen ist:
navigation.updateCurrentEntry({ state: newState });
Spezifikationen
Specification |
---|
HTML # navigation-api |