Start · Sprachen · JavaScript · Referenz · NavigationPrecommitController

NavigationPrecommitController

Schnittstelle

Wird einem <code>precommitHandler</code> aus <code>NavigateEvent.intercept()</code> übergeben und kann eine Navigation noch vor ihrem Commit umleiten, z. B. per <code>redirect()</code>.

seit JavaScript Navigation API (experimentell): Chromium 141+ (Chr Kategorie: webapi

Signatur

interface NavigationPrecommitController

Beschreibung

Das Interface NavigationPrecommitController gehört zur Navigation API. Sie bekommen nie selbst eine Instanz davon, sondern erhalten ein Objekt als einziges Argument, wenn Sie in NavigateEvent.intercept() die Option precommitHandler angeben. Der Browser ruft diesen Handler auf, bevor die Navigation festgeschrieben wird. Bis dahin haben sich die URL in der Adressleiste und navigation.currentEntry noch nicht geändert.

In dieser Phase können Sie noch entscheiden, wohin die Navigation geht. Die zentrale Methode dafür ist controller.redirect(url, options). Sie ändert das Ziel der laufenden Navigation und kann optional state, info und das Verlaufsverhalten (history) neu setzen. Anders als ein nachträgliches navigation.navigate() entsteht dabei kein zusätzlicher, ungewollter Eintrag im Verlauf.

Typische Einsätze sind:

  • Zugriffsprüfung: Nicht angemeldete Nutzer schicken Sie auf /login, bevor die geschützte URL überhaupt sichtbar wird.
  • Kanonische URLs: Veraltete Pfade oder Kurzlinks leiten Sie auf das richtige Ziel um.
  • Daten vorladen: Kritische Daten laden Sie vor dem Commit, damit die neue URL erst erscheint, wenn der Inhalt bereit ist.

Wenn das vom precommitHandler zurückgegebene Promise erfüllt wird, schreibt der Browser die Navigation fest. Danach läuft der normale handler. Wird das Promise abgelehnt, bricht die Navigation ab und navigateerror wird ausgelöst.

Parameter

Name Typ Default Beschreibung
$url Pflicht string | URL Parameter von redirect(): das neue Navigationsziel. Relative URLs werden gegen die Dokument-URL aufgelöst. Die URL muss denselben Ursprung haben wie das Dokument.
$options object {} Parameter von redirect(): optionales Objekt mit den folgenden Feldern.
$options.state any Neuer Zustand für den Verlaufseintrag. Er muss per structured clone kopierbar sein und ist später über navigation.currentEntry.getState() abrufbar.
$options.info any Flüchtige Zusatzinformation, die über NavigateEvent.info weitergereicht wird. Sie wird nicht im Verlauf gespeichert.
$options.history "auto" | "push" | "replace" "auto" Legt fest, ob die umgeleitete Navigation einen neuen Verlaufseintrag anlegt ("push") oder den aktuellen ersetzt ("replace").

Rückgabewert

Typ
void
Beschreibung
redirect() gibt undefined zurück. Die Wirkung zeigt sich nur darin, dass die laufende Navigation ein neues Ziel bekommt. Ob die Navigation abgeschlossen wird, hängt vom Promise ab, das der precommitHandler zurückgibt.

Beispiele

Geschützte Route: vor dem Commit auf /login umleiten

const isLoggedIn = () => document.cookie.includes('session=');

navigation.addEventListener('navigate', (event) => {
  const url = new URL(event.destination.url);

  // Nur eigene, abfangbare Navigationen unter /konto behandeln
  if (!event.canIntercept || !url.pathname.startsWith('/konto')) return;

  event.intercept({
    // Läuft VOR dem Commit – die Adressleiste zeigt noch die alte URL
    precommitHandler: async (controller) => {
      if (!isLoggedIn()) {
        const ziel = encodeURIComponent(url.pathname);
        controller.redirect(`/login?weiter=${ziel}`, { history: 'replace' });
      }
    },
    // Läuft NACH dem Commit mit der endgültigen URL
    handler: async () => {
      const pfad = new URL(navigation.currentEntry.url).pathname;
      document.querySelector('main').textContent = `Seite: ${pfad}`;
    }
  });
});

Veraltete URLs auf kanonische Pfade umschreiben

const umleitungen = new Map([
  ['/blog/alt-artikel', '/artikel/neuer-artikel'],
  ['/hilfe', '/support']
]);

navigation.addEventListener('navigate', (event) => {
  if (!event.canIntercept) return;
  const { pathname } = new URL(event.destination.url);
  const neu = umleitungen.get(pathname);
  if (!neu) return;

  event.intercept({
    precommitHandler: (controller) => {
      controller.redirect(neu, {
        state: { umgeleitetVon: pathname },
        info: { grund: 'kanonisch' }
      });
    },
    handler: async () => {
      console.log('Jetzt auf:', new URL(navigation.currentEntry.url).pathname);
      console.log('State:', navigation.currentEntry.getState());
    }
  });
});
Jetzt auf: /artikel/neuer-artikel State: { umgeleitetVon: '/blog/alt-artikel' }

Daten vor dem Commit laden, mit Feature-Erkennung

const unterstuetztPrecommit = 'NavigationPrecommitController' in window;

navigation.addEventListener('navigate', (event) => {
  if (!event.canIntercept || event.navigationType === 'reload') return;
  const url = new URL(event.destination.url);
  let daten;

  const laden = async () => {
    const res = await fetch(`/api${url.pathname}`, { signal: event.signal });
    if (res.status === 404) return null;
    return res.json();
  };

  event.intercept(unterstuetztPrecommit ? {
    precommitHandler: async (controller) => {
      daten = await laden();
      // Unbekannte Seite: auf die 404-Seite umleiten, statt eine kaputte URL festzuschreiben
      if (daten === null && event.navigationType !== 'traverse') {
        controller.redirect('/nicht-gefunden', { history: 'replace' });
      }
    },
    handler: async () => render(daten)
  } : {
    // Fallback ohne Precommit-Unterstützung
    handler: async () => render(await laden())
  });
});

const render = (daten) => {
  document.querySelector('main').textContent = daten ? daten.titel : 'Nicht gefunden';
};