Routing lato client moderno: API Navigation

Standardizzazione del routing lato client tramite una nuova API che rivede completamente la creazione di applicazioni a pagina singola.

Supporto dei browser

  • Chrome: 102.
  • Edge: 102.
  • Firefox: non supportato.
  • Safari: non supportato.

Origine

Le applicazioni a pagina singola, o APS, sono definite da una funzionalità principale: la riscrittura dinamica dei contenuti in base all'interazione dell'utente con il sito, anziché il metodo predefinito di caricamento di pagine completamente nuove dal server.

Anche se le APS sono state in grado di offrirti questa funzionalità tramite l'API History (o, in casi limitati, regolando la parte #hash del sito), si tratta di un'API inefficiente sviluppata molto prima che le SPA diventassero la norma e il web vuole un approccio completamente nuovo. L'API Navigation è un'API proposta che rivede completamente questo spazio, anziché cercare di correggere semplicemente le asperità dell'API History. Ad esempio, Scroll Restoration ha applicato patch all'API History anziché provare a reinventarla.

Questo post descrive l'API di navigazione a livello generale. Per leggere la proposta tecnica, consulta la bozza del report nel repository WICG.

Esempio di utilizzo

Per utilizzare l'API Navigation, inizia aggiungendo un ascoltatore "navigate" all'oggetto navigation globale. Questo evento è fondamentalmente centralizzato: viene attivato per tutti i tipi di navigazione, indipendentemente dal fatto che l'utente abbia eseguito un'azione (ad esempio facendo clic su un link, inviando un modulo o andando avanti e indietro) o quando la navigazione viene attivata tramite codice (ovvero tramite il codice del tuo sito). Nella maggior parte dei casi, consente al codice di sostituire il comportamento predefinito del browser per tale azione. Per le APS, questo probabilmente significa mantenere l'utente sulla stessa pagina e caricare o modificare i contenuti del sito.

All'ascoltatore "navigate" viene passato un NavigateEvent contenente informazioni sulla navigazione, ad esempio l'URL di destinazione, che ti consente di rispondere alla navigazione in un unico posto centralizzato. Un listener "navigate" di base potrebbe avere il seguente aspetto:

navigation.addEventListener('navigate', navigateEvent => {
  // Exit early if this navigation shouldn't be intercepted.
  // The properties to look at are discussed later in the article.
  if (shouldNotIntercept(navigateEvent)) return;

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

  if (url.pathname === '/') {
    navigateEvent.intercept({handler: loadIndexPage});
  } else if (url.pathname === '/cats/') {
    navigateEvent.intercept({handler: loadCatsPage});
  }
});

Puoi gestire la navigazione in due modi:

  • Chiamata a intercept({ handler }) (come descritto sopra) per gestire la navigazione.
  • Chiamare preventDefault(), che può annullare completamente la navigazione.

Questo esempio chiama intercept() sull'evento. Il browser chiama il tuo callback handler, che dovrebbe configurare lo stato successivo del tuo sito. Verrà creato un oggetto di transizione, navigation.transition, che può essere utilizzato da altro codice per monitorare l'avanzamento della navigazione.

In genere, intercept() e preventDefault() sono consentiti, ma si verificano casi in cui non è possibile chiamarli. Non puoi gestire le navigazioni tramite intercept() se si tratta di una navigazione tra origini. Inoltre, non puoi annullare una navigazione tramite preventDefault() se l'utente sta premendo i pulsanti Indietro o Avanti nel browser; non dovresti essere in grado di bloccare gli utenti sul tuo sito. (Questo argomento è in discussione su GitHub).

Anche se non puoi interrompere o intercettare la navigazione stessa, l'evento "navigate" verrà comunque attivato. Poiché è informativo, il tuo codice potrebbe, ad esempio, registrare un evento Analytics per indicare che un utente sta uscendo dal sito.

Perché aggiungere un altro evento alla piattaforma?

Un listener di eventi "navigate" centralizza la gestione delle modifiche agli URL all'interno di un'APS. Si tratta di una proposta difficile da utilizzare con le API precedenti. Se hai mai scritto il routing per la tua SPA utilizzando l'API History, potresti aver aggiunto codice come questo:

function updatePage(event) {
  event.preventDefault(); // we're handling this link
  window.history.pushState(null, '', event.target.href);
  // TODO: set up page based on new URL
}
const links = [...document.querySelectorAll('a[href]')];
links.forEach(link => link.addEventListener('click', updatePage));

Questo è accettabile, ma non esaustivo. I link possono entrare e uscire dalla tua pagina e non sono l'unico modo in cui gli utenti possono navigare tra le pagine. Ad esempio, possono inviare un modulo o persino utilizzare una mappa di immagini. La tua pagina potrebbe gestire questi casi, ma esiste una lunga serie di possibilità che potrebbero essere semplificate, cosa che la nuova API Navigation consente di fare.

Inoltre, quanto sopra non gestisce la navigazione avanti/indietro. C'è un altro evento per questo, "popstate".

Personalmente, l'API History spesso sembra poter contribuire a queste possibilità. Tuttavia, ha solo due aree di superficie: risponde se l'utente preme Indietro o Avanti nel browser, oltre a inviare e sostituire gli URL. Non ha un'analogia con "navigate", ad eccezione del caso in cui configuri manualmente gli ascoltatori per gli eventi di clic, ad esempio, come dimostrato sopra.

Decidere come gestire una navigazione

navigateEvent contiene molte informazioni sulla navigazione che puoi utilizzare per decidere come gestire una determinata navigazione.

Le proprietà principali sono:

canIntercept
Se il valore è falso, non puoi intercettare la navigazione. Le navigazioni tra origini e i traversali tra documenti non possono essere intercettati.
destination.url
Probabilmente l'informazione più importante da considerare quando si gestisce la navigazione.
hashChange
True se la navigazione avviene nello stesso documento e l'hash è l'unica parte dell'URL diversa dall'URL corrente. Nelle SPA moderne, l'hash deve essere utilizzato per i link a diverse parti del documento corrente. Pertanto, se hashChange è true, probabilmente non è necessario intercettare questa navigazione.
downloadRequest
Se questo è vero, la navigazione è stata avviata da un link con un attributo download. Nella maggior parte dei casi, non è necessario intercettare questa richiesta.
formData
Se non è nullo, questa navigazione fa parte di un invio di un modulo POST. Assicurati di tenere conto di questo aspetto quando gestisci la navigazione. Se vuoi gestire solo le navigazioni GET, evita di intercettare le navigazioni in cui formData non è null. Consulta l'esempio sulla gestione degli invii di moduli più avanti nell'articolo.
navigationType
Deve essere uno dei valori "reload", "push", "replace" o "traverse". Se è "traverse", questa navigazione non può essere annullata tramite preventDefault().

Ad esempio, la funzione shouldNotIntercept utilizzata nel primo esempio potrebbe essere simile alla seguente:

function shouldNotIntercept(navigationEvent) {
  return (
    !navigationEvent.canIntercept ||
    // If this is just a hashChange,
    // just let the browser handle scrolling to the content.
    navigationEvent.hashChange ||
    // If this is a download,
    // let the browser perform the download.
    navigationEvent.downloadRequest ||
    // If this is a form submission,
    // let that go to the server.
    navigationEvent.formData
  );
}

Intercettazione

Quando il codice chiama intercept({ handler }) dall'interno dell'ascoltatore "navigate", informa il browser che sta preparando la pagina per il nuovo stato aggiornato e che la navigazione potrebbe richiedere del tempo.

Il browser inizia acquisendo la posizione di scorrimento per lo stato corrente, in modo che possa essere ripristinata in un secondo momento, quindi chiama il tuo callback handler. Se handler restituisce una promessa (cosa che accade automaticamente con le funzioni asincrone), questa promessa indica al browser il tempo necessario per la navigazione e se l'operazione è andata a buon fine.

navigation.addEventListener('navigate', navigateEvent => {
  if (shouldNotIntercept(navigateEvent)) return;
  const url = new URL(navigateEvent.destination.url);

  if (url.pathname.startsWith('/articles/')) {
    navigateEvent.intercept({
      async handler() {
        const articleContent = await getArticleContent(url.pathname);
        renderArticlePage(articleContent);
      },
    });
  }
});

Di conseguenza, questa API introduce un concetto semantico compreso dal browser: al momento è in corso la navigazione in un'applicazione SPA, che nel tempo modifica il documento da un URL e uno stato precedenti a uno nuovo. Ciò presenta una serie di potenziali vantaggi, tra cui l'accessibilità: i browser possono mostrare l'inizio, la fine o il potenziale errore di una navigazione. Ad esempio, Chrome attiva l'indicatore di caricamento nativo e consente all'utente di interagire con il pulsante di interruzione. Al momento, questo non accade quando l'utente naviga tramite i pulsanti Indietro/Avanti, ma il problema verrà risolto a breve.

Quando intercetti le navigazioni, il nuovo URL verrà applicato appena prima della chiamata del callback handler. Se non aggiorni immediatamente il DOM, si crea un periodo in cui i vecchi contenuti vengono visualizzati insieme al nuovo URL. Ciò influisce su aspetti quali la risoluzione degli URL relativi durante il recupero dei dati o il caricamento di nuove risorse secondarie.

Un modo per ritardare la modifica dell'URL è discututo su GitHub, ma in genere è consigliabile aggiornare immediatamente la pagina con una sorta di segnaposto per i contenuti in arrivo:

navigation.addEventListener('navigate', navigateEvent => {
  if (shouldNotIntercept(navigateEvent)) return;
  const url = new URL(navigateEvent.destination.url);

  if (url.pathname.startsWith('/articles/')) {
    navigateEvent.intercept({
      async handler() {
        // The URL has already changed, so quickly show a placeholder.
        renderArticlePagePlaceholder();
        // Then fetch the real data.
        const articleContent = await getArticleContent(url.pathname);
        renderArticlePage(articleContent);
      },
    });
  }
});

Questo non solo evita i problemi di risoluzione degli URL, ma risulta anche veloce perché rispondi immediatamente all'utente.

Segnali di interruzione

Poiché puoi eseguire operazioni asincrone in un gestore intercept(), è possibile che la navigazione risulti ridondante. Ciò si verifica quando:

  • L'utente fa clic su un altro link o un codice esegue un'altra navigazione. In questo caso, la navigazione precedente viene abbandonata a favore della nuova.
  • L'utente fa clic sul pulsante "Interrompi" nel browser.

Per gestire una di queste possibilità, l'evento passato al listener "navigate" contiene una proprietà signal, ovvero AbortSignal. Per ulteriori informazioni, consulta la sezione Recupero annullabile.

In breve, fornisce un oggetto che attiva un evento quando devi interrompere il lavoro. In particolare, puoi passare AbortSignal a qualsiasi chiamata che effettui a fetch(), di conseguenza le richieste di rete in volo verranno annullate se la navigazione viene prerilasciata. In questo modo, risparmierai la larghezza di banda dell'utente e rifiuterai il valore Promise restituito da fetch(), impedendo a qualsiasi codice successivo di eseguire azioni come l'aggiornamento del DOM per mostrare una navigazione nella pagina non valida.

Ecco l'esempio precedente, ma con getArticleContent in linea, che mostra come AbortSignal può essere utilizzato con fetch():

navigation.addEventListener('navigate', navigateEvent => {
  if (shouldNotIntercept(navigateEvent)) return;
  const url = new URL(navigateEvent.destination.url);

  if (url.pathname.startsWith('/articles/')) {
    navigateEvent.intercept({
      async handler() {
        // The URL has already changed, so quickly show a placeholder.
        renderArticlePagePlaceholder();
        // Then fetch the real data.
        const articleContentURL = new URL(
          '/get-article-content',
          location.href
        );
        articleContentURL.searchParams.set('path', url.pathname);
        const response = await fetch(articleContentURL, {
          signal: navigateEvent.signal,
        });
        const articleContent = await response.json();
        renderArticlePage(articleContent);
      },
    });
  }
});

Gestione dello scorrimento

Quando intercept() una navigazione, il browser tenterà di gestire lo scorrimento automaticamente.

Per le navigazioni a una nuova voce della cronologia (quando navigationEvent.navigationType è "push" o "replace"), significa tentare di scorrere fino alla parte indicata dal frammento di URL (il bit dopo #) o reimpostare lo scorrimento nella parte superiore della pagina.

Per i ricaricamenti e le esplorazioni, significa ripristinare la posizione di scorrimento in cui si trovava l'ultima volta che è stata visualizzata questa voce della cronologia.

Per impostazione predefinita, ciò avviene una volta risolta la promessa restituita da handler, ma se ha senso scorrere prima, puoi chiamare navigateEvent.scroll():

navigation.addEventListener('navigate', navigateEvent => {
  if (shouldNotIntercept(navigateEvent)) return;
  const url = new URL(navigateEvent.destination.url);

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

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

In alternativa, puoi disattivare completamente la gestione automatica dello scorrimento impostando l'opzione scroll di intercept() su "manual":

navigateEvent.intercept({
  scroll: 'manual',
  async handler() {
    // …
  },
});

Gestione dell'attenzione

Una volta risolta la promessa restituita da handler, il browser concentrerà il primo elemento con l'attributo autofocus impostato o l'elemento <body> se nessun elemento ha questo attributo.

Puoi disattivare questo comportamento impostando l'opzione focusReset di intercept() su "manual":

navigateEvent.intercept({
  focusReset: 'manual',
  async handler() {
    // …
  },
});

Eventi di successo e fallimento

Quando viene chiamato l'handler intercept(), si verifica una delle due seguenti situazioni:

  • Se l'oggetto Promise restituito viene soddisfatto (o non hai chiamato intercept()), l'API di navigazione attiverà "navigatesuccess" con un Event.
  • Se il Promise restituito viene rifiutato, l'API attiverà "navigateerror" con un ErrorEvent.

Questi eventi consentono al codice di gestire il successo o l'errore in modo centralizzato. Ad esempio, puoi gestire l'esito positivo nascondendo un indicatore di avanzamento visualizzato in precedenza, come questo:

navigation.addEventListener('navigatesuccess', event => {
  loadingIndicator.hidden = true;
});

In alternativa, in caso di errore potresti visualizzare un messaggio di errore:

navigation.addEventListener('navigateerror', event => {
  loadingIndicator.hidden = true; // also hide indicator
  showMessage(`Failed to load page: ${event.message}`);
});

L'ascoltatore di eventi "navigateerror", che riceve un ErrorEvent, è particolarmente utile in quanto riceverà sicuramente eventuali errori dal codice che configura una nuova pagina. Puoi semplicemente await fetch() sapendo che, se la rete non è disponibile, l'errore verrà inoltrato a "navigateerror".

navigation.currentEntry fornisce l'accesso alla voce corrente. Si tratta di un oggetto che descrive la posizione attuale dell'utente. Questa voce include l'URL corrente, i metadati che possono essere utilizzati per identificare questa voce nel tempo e lo stato fornito dallo sviluppatore.

I metadati includono key, una proprietà stringa univoca di ogni voce che rappresenta la voce corrente e il relativo slot. Questa chiave rimane invariata anche se l'URL o lo stato della voce corrente cambiano. Si trova ancora nello stesso spazio. Al contrario, se un utente preme Indietro e poi riapre la stessa pagina, key cambierà perché questa nuova voce crea un nuovo spazio.

Per uno sviluppatore, key è utile perché l'API Navigation consente di indirizzare direttamente l'utente a una voce con una chiave corrispondente. Puoi conservarlo, anche negli stati di altre voci, per passare facilmente da una pagina all'altra.

// 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;

Stato

L'API Navigation mostra un concetto di "stato", ovvero informazioni fornite dallo sviluppatore che vengono archiviate in modo permanente nella voce della cronologia corrente, ma che non sono direttamente visibili all'utente. È molto simile, ma migliorato rispetto a history.state nell'API History.

Nell'API di navigazione, puoi chiamare il metodo .getState() della voce corrente (o di qualsiasi voce) per restituire una copia del suo stato:

console.log(navigation.currentEntry.getState());

Per impostazione predefinita, sarà undefined.

Stato impostazione

Sebbene gli oggetti dello stato possano essere sottoposti a mutazioni, queste modifiche non vengono salvate di nuovo con la voce della cronologia, quindi:

const state = navigation.currentEntry.getState();
console.log(state.count); // 1
state.count++;
console.log(state.count); // 2
// But:
console.info(navigation.currentEntry.getState().count); // will still be 1

Il modo corretto per impostare lo stato è durante la navigazione nello script:

navigation.navigate(url, {state: newState});
// Or:
navigation.reload({state: newState});

Dove newState può essere qualsiasi oggetto clonabile.

Se vuoi aggiornare lo stato della voce corrente, è meglio eseguire una navigazione che la sostituisca:

navigation.navigate(location.href, {state: newState, history: 'replace'});

L'ascoltatore di eventi "navigate" può rilevare questa modifica tramite navigateEvent.destination:

navigation.addEventListener('navigate', navigateEvent => {
  console.log(navigateEvent.destination.getState());
});

Aggiornamento dello stato sincrono

In genere, è meglio aggiornare lo stato in modo asincrono tramite navigation.reload({state: newState}), in modo che l'ascoltatore "navigate" possa applicarlo. Tuttavia, a volte il cambiamento di stato è già stato completamente applicato quando il codice viene a conoscenza del problema, ad esempio quando l'utente attiva/disattiva un elemento <details> o cambia lo stato di un input di modulo. In questi casi, ti consigliamo di aggiornare lo stato in modo che queste modifiche vengano conservate durante i ricaricamenti e le esplorazioni. Questo è possibile utilizzando updateCurrentEntry():

navigation.updateCurrentEntry({state: newState});

C'è anche un evento per conoscere questa modifica:

navigation.addEventListener('currententrychange', () => {
  console.log(navigation.currentEntry.getState());
});

Tuttavia, se ti trovi a reagire alle modifiche dello stato in "currententrychange", potresti suddividere o addirittura duplicare il codice di gestione dello stato tra l'evento "navigate" e l'evento "currententrychange", mentre navigation.reload({state: newState}) ti consentirebbe di gestirlo in un unico posto.

Stato e parametri URL

Poiché lo stato può essere un oggetto strutturato, si potrebbe avere la tentazione di utilizzarlo per tutto lo stato dell'applicazione. Tuttavia, in molti casi è meglio memorizzare questo stato nell'URL.

Se prevedi che lo stato venga mantenuto quando l'utente condivide l'URL con un altro utente, memorizzalo nell'URL. In caso contrario, l'oggetto stato è l'opzione migliore.

Accedi a tutte le voci

La "voce corrente" non è tutto. L'API fornisce anche un modo per accedere all'intero elenco di voci che un utente ha visualizzato durante l'utilizzo del tuo sito tramite la chiamata navigation.entries(), che restituisce un array istantaneo di voci. Questa funzionalità consente, ad esempio, di mostrare una UI diversa in base al modo in cui l'utente ha raggiunto una determinata pagina oppure semplicemente di esaminare gli URL o i relativi stati precedenti. Ciò è impossibile con l'attuale API History.

Puoi anche ascoltare un evento "dispose" sui singoli NavigationHistoryEntry, che viene attivato quando la voce non fa più parte della cronologia del browser. Questo può accadere durante una pulizia generale, ma anche durante la navigazione. Ad esempio, se torni indietro di 10 posizioni e poi navighi in avanti, queste 10 voci della cronologia verranno eliminate.

Esempi

L'evento "navigate" viene attivato per tutti i tipi di navigazione, come indicato sopra. (Nella specifica è presente un lungo allegato di tutti i tipi possibili).

Sebbene per molti siti la situazione più comune sia quando l'utente fa clic su un <a href="...">, esistono due tipi di navigazione più complessi e importanti che vale la pena prendere in considerazione.

Navigazione programmatica

La prima è la navigazione programmatica, in cui la navigazione è causata da una chiamata di metodo all'interno del codice lato client.

Puoi chiamare navigation.navigate('/another_page') da qualsiasi punto del codice per eseguire una navigazione. Questo verrà gestito dal listener di eventi centralizzato registrato sul listener "navigate" e il listener centralizzato verrà chiamato in modo sincrono.

Si tratta di un'aggregazione migliorata di metodi precedenti come location.assign() e amici, oltre ai metodi pushState() e replaceState() dell'API History.

Il metodo navigation.navigate() restituisce un oggetto contenente due istanze Promise in { committed, finished }. In questo modo, l'autore dell'invocazione può attendere che la transizione sia "committata" (l'URL visibile è cambiato ed è disponibile un nuovo NavigationHistoryEntry) o "terminata" (tutte le promesse restituite da NavigationHistoryEntry sono complete o rifiutate a causa di un errore o perché sono state anticipate da un'altra navigazione).

Il metodo navigate ha anche un oggetto options, in cui puoi impostare:

  • state: lo stato della nuova voce della cronologia, disponibile tramite il metodo .getState() in NavigationHistoryEntry.
  • history: che può essere impostato su "replace" per sostituire la voce attuale della cronologia.
  • info: un oggetto da passare all'evento di navigazione tramite navigateEvent.info.

In particolare, info potrebbe essere utile, ad esempio, per indicare una determinata animazione che fa apparire la pagina successiva. L'alternativa potrebbe essere impostare una variabile globale o includerla nell'#hash. Entrambe le opzioni sono un po' complicate. In particolare, questo info non verrà riprodotto se in un secondo momento un utente attiva la navigazione, ad esempio tramite i pulsanti Indietro e Avanti. In questi casi, infatti, sarà sempre undefined.

Demo dell'apertura da sinistra o da destra

navigation dispone anche di una serie di altri metodi di navigazione, che restituiscono tutti un oggetto contenente { committed, finished }. Ho già menzionato traverseTo() (che accetta uno key che indica una voce specifica nella cronologia dell'utente) e navigate(). Sono inclusi anche back(), forward() e reload(). Questi metodi vengono gestiti, come navigate(), dal listener di eventi "navigate" centralizzato.

Invii di moduli

In secondo luogo, l'invio di HTML <form> tramite POST è un tipo speciale di navigazione e l'API Navigation può intercettarlo. Sebbene includa un payload aggiuntivo, la navigazione viene comunque gestita centralmente dall'ascoltatore "navigate".

L'invio del modulo può essere rilevato cercando la proprietà formData in NavigateEvent. Ecco un esempio che trasforma semplicemente qualsiasi modulo inviato in uno che rimane nella pagina corrente tramite fetch():

navigation.addEventListener('navigate', navigateEvent => {
  if (navigateEvent.formData && navigateEvent.canIntercept) {
    // User submitted a POST form to a same-domain URL
    // (If canIntercept is false, the event is just informative:
    // you can't intercept this request, although you could
    // likely still call .preventDefault() to stop it completely).

    navigateEvent.intercept({
      // Since we don't update the DOM in this navigation,
      // don't allow focus or scrolling to reset:
      focusReset: 'manual',
      scroll: 'manual',
      handler() {
        await fetch(navigateEvent.destination.url, {
          method: 'POST',
          body: navigateEvent.formData,
        });
        // You could navigate again with {history: 'replace'} to change the URL here,
        // which might indicate "done"
      },
    });
  }
});

Cosa manca?

Nonostante la natura centralizzata dell'ascoltatore di eventi "navigate", la specifica dell'API Navigation attuale non attiva "navigate" al primo caricamento di una pagina. Per i siti che utilizzano il rendering lato server (SSR) per tutti gli stati, questo potrebbe non essere un problema: il tuo server potrebbe restituire lo stato iniziale corretto, che è il modo più veloce per fornire contenuti agli utenti. Tuttavia, i siti che utilizzano il codice lato client per creare le pagine potrebbero dover creare una funzione aggiuntiva per inizializzarle.

Un'altra scelta di progettazione intenzionale dell'API Navigation è che opera solo all'interno di un singolo frame, ovvero la pagina di primo livello o un singolo <iframe> specifico. Ciò ha una serie di implicazioni interessanti che sono documentate ulteriormente nella specifica, ma in pratica ridurrà la confusione degli sviluppatori. L'API History precedente presenta una serie di casi limite confusi, come il supporto dei frame, mentre la nuova API Navigation gestisce questi casi limite fin dall'inizio.

Infine, non c'è ancora un consenso sulla modifica o sul riordinamento tramite programmazione dell'elenco delle voci che l'utente ha visualizzato. Al momento è in corso la discussione, ma un'opzione potrebbe essere consentire solo le eliminazioni: voci storiche o "tutte le voci future". Quest'ultimo consentirebbe lo stato temporaneo. Ad esempio, in qualità di sviluppatore, potrei:

  • Chiedi all'utente una domanda passando a un nuovo URL o stato
  • Consentire all'utente di completare il lavoro (o tornare indietro)
  • rimuovere una voce della cronologia al completamento di un'attività

Questa opzione potrebbe essere perfetta per modali o interstitial temporanei: l'utente può utilizzare il gesto Indietro per uscire dal nuovo URL, ma non può andare accidentalmente Avanti per aprirlo di nuovo (poiché la voce è stata rimossa). Questo non è possibile con l'attuale API History.

Prova l'API Navigation

L'API Navigation è disponibile in Chrome 102 senza flag. Puoi anche provare una demo di Domenic Denicola.

Sebbene l'API History classica sembri semplice, non è molto ben definita e presenta un numero elevato di problemi relativi ai casi limite e al modo in cui è stata implementata in modo diverso nei vari browser. Ci auguriamo che tu voglia inviarci un feedback sulla nuova API Navigation.

Riferimenti

Ringraziamenti

Ringraziamo Thomas Steiner, Domenic Denicola e Nate Chapin per aver recensito questo post.