Het standaardiseren van client-side routing via een gloednieuwe API die de ontwikkeling van single-page applicaties volledig op zijn kop zet.
Single-page applicaties, ofwel SPA's, worden gekenmerkt door een kerneigenschap: het dynamisch herschrijven van de inhoud naarmate de gebruiker interactie heeft met de site, in plaats van de standaardmethode waarbij volledig nieuwe pagina's van de server worden geladen.
Hoewel SPA's deze functionaliteit al konden bieden via de History API (of in beperkte gevallen door het #hash-gedeelte van de site aan te passen), is het een onhandige API die lang geleden is ontwikkeld, voordat SPA's de norm waren. Het web schreeuwt om een compleet nieuwe aanpak. De Navigation API is een voorgestelde API die dit gebied volledig vernieuwt, in plaats van simpelweg de ruwe kantjes van de History API op te vullen. ( Scroll Restoration heeft bijvoorbeeld de History API bijgewerkt in plaats van deze opnieuw uit te vinden.)
Dit bericht beschrijft de navigatie-API op hoofdlijnen. Voor het volledige technische voorstel kunt u het conceptrapport in de WICG-repository raadplegen.
Voorbeeldgebruik
Om de Navigation API te gebruiken, begin je met het toevoegen van een "navigate" -listener aan het globale navigation . Deze gebeurtenis is in principe gecentraliseerd : hij wordt geactiveerd voor alle soorten navigatie, of de gebruiker nu een actie heeft uitgevoerd (zoals klikken op een link, het verzenden van een formulier of terug- en vooruitnavigeren) of wanneer de navigatie programmatisch wordt geactiveerd (d.w.z. via de code van je website). In de meeste gevallen stelt dit je code in staat om het standaardgedrag van de browser voor die actie te overschrijven. Voor SPA's betekent dit waarschijnlijk dat de gebruiker op dezelfde pagina blijft en de inhoud van de website wordt geladen of gewijzigd.
Een NavigateEvent wordt doorgegeven aan de "navigate" -listener. Deze bevat informatie over de navigatie, zoals de bestemmings-URL, en stelt je in staat om op één centrale plek op de navigatie te reageren. Een eenvoudige "navigate" -listener zou er als volgt uit kunnen zien:
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});
}
});
Je kunt de navigatie op twee manieren aanpakken:
- De
intercept({ handler })(zoals hierboven beschreven) wordt aangeroepen om de navigatie af te handelen. - Door
preventDefault()aan te roepen, kan de navigatie volledig worden geannuleerd.
Dit voorbeeld roept intercept() aan op de gebeurtenis. De browser roept vervolgens uw handler callback aan, die de volgende status van uw site moet configureren. Dit creëert een overgangsobject, navigation.transition , dat andere code kan gebruiken om de voortgang van de navigatie te volgen.
Zowel intercept() als preventDefault() zijn doorgaans toegestaan, maar er zijn gevallen waarin ze niet kunnen worden aangeroepen. Je kunt navigaties niet afhandelen via intercept() als het een cross-origin navigatie betreft. En je kunt een navigatie niet annuleren via preventDefault() als de gebruiker op de Terug- of Vooruit-knop in zijn browser drukt; je zou je gebruikers niet op je site moeten kunnen vastzetten. (Dit wordt besproken op GitHub .)
Zelfs als je de navigatie zelf niet kunt stoppen of onderscheppen, wordt de "navigate" -gebeurtenis toch geactiveerd. Deze gebeurtenis is informatief , dus je code zou bijvoorbeeld een Analytics-gebeurtenis kunnen registreren om aan te geven dat een gebruiker je site verlaat.
Waarom nog een evenement aan het platform toevoegen?
Een eventlistener "navigate" centraliseert de afhandeling van URL-wijzigingen binnen een SPA. Dit is lastig met oudere API's. Als je ooit zelf de routing voor je SPA hebt geschreven met behulp van de History API, heb je wellicht code zoals deze toegevoegd:
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));
Dit is prima, maar niet volledig. Links kunnen op je pagina verschijnen en weer verdwijnen, en ze zijn niet de enige manier waarop gebruikers door pagina's kunnen navigeren. Ze kunnen bijvoorbeeld een formulier invullen of zelfs een imagemap gebruiken. Je pagina kan hiermee omgaan, maar er zijn talloze mogelijkheden die vereenvoudigd zouden kunnen worden – iets wat de nieuwe Navigation API mogelijk maakt.
Bovendien is bovenstaande code niet geschikt voor navigatie terug/vooruit. Daarvoor is er een aparte gebeurtenis, namelijk "popstate" .
Persoonlijk heb ik vaak het gevoel dat de History API een stap in de goede richting zou kunnen zijn bij deze mogelijkheden. De API heeft echter eigenlijk maar twee toepassingsgebieden: reageren wanneer de gebruiker op Terug of Vooruit drukt in de browser, en URL's pushen en vervangen. Er is geen equivalent voor "navigate" , tenzij je handmatig listeners instelt voor klikgebeurtenissen, zoals hierboven is gedemonstreerd.
Beslissen hoe een navigatie moet worden aangepakt
De navigateEvent bevat veel informatie over de navigatie die je kunt gebruiken om te bepalen hoe je met een bepaalde navigatie omgaat.
De belangrijkste eigenschappen zijn:
-
canIntercept - Als dit onjuist is, kunt u de navigatie niet onderscheppen. Navigatie tussen verschillende bronnen en het doorlopen van meerdere documenten kunnen niet worden onderschept.
-
destination.url - Dit is waarschijnlijk de belangrijkste informatie om rekening mee te houden bij het navigeren.
-
hashChange - Dit is waar als de navigatie binnen hetzelfde document plaatsvindt en de hash het enige deel van de URL is dat verschilt van de huidige URL. In moderne SPA's zou de hash moeten dienen om naar verschillende delen van het huidige document te linken. Dus als
hashChangewaar is, hoeft u deze navigatie waarschijnlijk niet te onderscheppen. -
downloadRequest - Als dit klopt, is de navigatie gestart via een link met een
downloadattribuut. In de meeste gevallen hoeft u dit niet te onderscheppen. -
formData - Als dit niet null is, maakt deze navigatie deel uit van een POST-formulierinzending. Houd hier rekening mee bij het afhandelen van de navigatie. Als u alleen GET-navigaties wilt afhandelen, vermijd dan het onderscheppen van navigaties waarbij
formDataniet null is. Zie het voorbeeld over het afhandelen van formulierinzendingen verderop in dit artikel. -
navigationType - Dit is een van de volgende acties:
"reload","push","replace"of"traverse". Als het"traverse"is, kan deze navigatie niet worden geannuleerd viapreventDefault().
De functie shouldNotIntercept die in het eerste voorbeeld wordt gebruikt, zou er bijvoorbeeld zo uit kunnen zien:
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
);
}
Onderscheppen
Wanneer uw code vanuit de "navigate" -listener intercept({ handler }) aanroept, laat deze de browser weten dat de pagina wordt voorbereid op de nieuwe, bijgewerkte status en dat de navigatie enige tijd kan duren.
De browser begint met het vastleggen van de scrollpositie voor de huidige status, zodat deze later eventueel kan worden hersteld. Vervolgens roept de browser de callbackfunctie van je handler aan. Als je handler een promise retourneert (wat automatisch gebeurt bij asynchrone functies ), vertelt die promise de browser hoe lang de navigatie duurt en of deze succesvol is.
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);
},
});
}
});
Deze API introduceert een semantisch concept dat de browser begrijpt: er vindt momenteel een SPA-navigatie plaats, waarbij het document in de loop van de tijd verandert van een vorige URL en status naar een nieuwe. Dit heeft een aantal potentiële voordelen, waaronder toegankelijkheid: browsers kunnen het begin, het einde of een mogelijke mislukking van een navigatie weergeven. Chrome activeert bijvoorbeeld de native laadindicator en stelt de gebruiker in staat om te interageren met de stopknop. (Dit gebeurt momenteel niet wanneer de gebruiker navigeert via de terug-/vooruitknoppen, maar dat zal binnenkort worden opgelost .)
Navigatie die zich inzet
Bij het onderscheppen van navigaties wordt de nieuwe URL van kracht net voordat de callbackfunctie van je handler wordt aangeroepen. Als je de DOM niet direct bijwerkt, ontstaat er een periode waarin de oude inhoud samen met de nieuwe URL wordt weergegeven. Dit heeft gevolgen voor zaken zoals relatieve URL-resolutie bij het ophalen van gegevens of het laden van nieuwe subbronnen.
Er wordt op GitHub gediscussieerd over een manier om de URL-wijziging uit te stellen, maar over het algemeen wordt aangeraden om de pagina direct bij te werken met een placeholder voor de binnenkomende inhoud:
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);
},
});
}
});
Dit voorkomt niet alleen problemen met URL-resolutie, maar het voelt ook snel aan omdat je direct op de gebruiker reageert.
Annuleringssignalen
Omdat je asynchroon werk kunt uitvoeren in een intercept() handler, kan de navigatie overbodig worden. Dit gebeurt wanneer:
- De gebruiker klikt op een andere link, of er wordt een andere navigatie uitgevoerd door de code. In dat geval wordt de oude navigatie overgeslagen ten gunste van de nieuwe navigatie.
- De gebruiker klikt op de 'stop'-knop in de browser.
Om met al deze mogelijkheden om te gaan, bevat de gebeurtenis die aan de "navigate" -listener wordt doorgegeven een signal , namelijk een AbortSignal . Zie Abortable fetch voor meer informatie.
Kort gezegd biedt het een object dat een gebeurtenis activeert wanneer je je werk moet stoppen. Je kunt bijvoorbeeld een AbortSignal doorgeven aan alle aanroepen van fetch() , waardoor lopende netwerkverzoeken worden geannuleerd als de navigatie wordt onderbroken. Dit bespaart de gebruiker bandbreedte en zorgt ervoor dat de Promise die door fetch() wordt geretourneerd, wordt afgewezen. Hierdoor wordt voorkomen dat volgende code acties uitvoert, zoals het bijwerken van de DOM om een ongeldige paginanavigatie weer te geven.
Hier is het vorige voorbeeld, maar dan met getArticleContent inline, waarmee wordt getoond hoe het AbortSignal kan worden gebruikt met 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);
},
});
}
});
Scrollafhandeling
Wanneer je een navigatie intercept() , zal de browser proberen het scrollen automatisch af te handelen.
Bij navigatie naar een nieuw geschiedenisitem (wanneer navigationEvent.navigationType "push" of "replace" is), betekent dit dat er geprobeerd wordt te scrollen naar het gedeelte dat wordt aangegeven door het URL-fragment (het gedeelte na de # ), of dat het scrollen wordt teruggezet naar de bovenkant van de pagina.
Bij het herladen en navigeren door de geschiedenis betekent dit dat de scrollpositie wordt hersteld naar de positie waar deze zich bevond toen dit geschiedenisitem voor het laatst werd weergegeven.
Standaard gebeurt dit zodra de promise die door je handler wordt geretourneerd, is opgelost. Maar als het zinvol is om eerder te scrollen, kun je navigateEvent.scroll() aanroepen:
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);
},
});
}
});
Als alternatief kunt u de automatische scrollverwerking volledig uitschakelen door de scroll van intercept() in te stellen op "manual" :
navigateEvent.intercept({
scroll: 'manual',
async handler() {
// …
},
});
Focusbeheer
Zodra de promise die door je handler wordt geretourneerd is opgelost, zal de browser het eerste element met het autofocus attribuut focussen, of het <body> -element als geen enkel element dat attribuut heeft.
Je kunt dit gedrag uitschakelen door de focusReset optie van intercept() in te stellen op "manual" :
navigateEvent.intercept({
focusReset: 'manual',
async handler() {
// …
},
});
Succesvolle en mislukte gebeurtenissen
Wanneer uw intercept() -handler wordt aangeroepen, zullen er twee dingen gebeuren:
- Als de geretourneerde
Promisewordt vervuld (of als jeintercept()niet hebt aangeroepen), zal de Navigation API"navigatesuccess"activeren met eenEvent. - Als de geretourneerde
Promisewordt afgewezen, zal de API"navigateerror"met eenErrorEventactiveren.
Deze gebeurtenissen stellen uw code in staat om succes of mislukking op een gecentraliseerde manier af te handelen. U kunt bijvoorbeeld bij succes een eerder weergegeven voortgangsindicator verbergen, zoals in dit voorbeeld:
navigation.addEventListener('navigatesuccess', event => {
loadingIndicator.hidden = true;
});
Of je kunt bij een storing een foutmelding weergeven:
navigation.addEventListener('navigateerror', event => {
loadingIndicator.hidden = true; // also hide indicator
showMessage(`Failed to load page: ${event.message}`);
});
De eventlistener "navigateerror" , die een ErrorEvent ontvangt, is bijzonder handig omdat deze gegarandeerd alle fouten van je code ontvangt die een nieuwe pagina aan het opzetten is. Je kunt simpelweg await fetch() in de wetenschap dat als het netwerk niet beschikbaar is, de fout uiteindelijk naar "navigateerror" wordt doorgestuurd.
Navigatie-items
navigation.currentEntry biedt toegang tot de huidige entry. Dit is een object dat beschrijft waar de gebruiker zich op dit moment bevindt. Deze entry bevat de huidige URL, metadata waarmee deze entry in de loop van de tijd kan worden geïdentificeerd, en door de ontwikkelaar verstrekte statusinformatie.
De metadata bevat key , een unieke tekenreekseigenschap van elke vermelding die de huidige vermelding en de bijbehorende positie vertegenwoordigt. Deze sleutel blijft hetzelfde, zelfs als de URL of de status van de huidige vermelding verandert. De vermelding blijft in dezelfde positie. Omgekeerd, als een gebruiker op Terug drukt en vervolgens dezelfde pagina opnieuw opent, verandert key , omdat deze nieuwe vermelding een nieuwe positie creëert.
Voor een ontwikkelaar is key handig omdat de Navigation API het mogelijk maakt om de gebruiker direct naar een item met een overeenkomende sleutel te leiden. Je kunt de sleutel bewaren, zelfs in de verschillende statussen van andere items, om zo gemakkelijk tussen pagina's te kunnen schakelen.
// 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;
Staat
De Navigation API introduceert het concept "status", oftewel door de ontwikkelaar verstrekte informatie die permanent wordt opgeslagen bij het huidige geschiedenisitem, maar die niet direct zichtbaar is voor de gebruiker. Dit is zeer vergelijkbaar met, maar verbeterd ten opzichte van, history.state in de History API.
In de Navigation API kunt u de .getState() methode van het huidige item (of een willekeurig item) aanroepen om een kopie van de status ervan te verkrijgen:
console.log(navigation.currentEntry.getState());
Standaard is dit undefined .
Instellen van de status
Hoewel statusobjecten kunnen worden gewijzigd, worden die wijzigingen niet opgeslagen in de geschiedenisvermelding, dus:
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
De juiste manier om de status in te stellen is tijdens de scriptnavigatie:
navigation.navigate(url, {state: newState});
// Or:
navigation.reload({state: newState});
Waarbij newState elk kloonbaar object kan zijn.
Als u de status van het huidige item wilt bijwerken, kunt u het beste een navigatie uitvoeren die het huidige item vervangt:
navigation.navigate(location.href, {state: newState, history: 'replace'});
Vervolgens kan uw eventlistener voor "navigate" deze wijziging oppikken via navigateEvent.destination :
navigation.addEventListener('navigate', navigateEvent => {
console.log(navigateEvent.destination.getState());
});
Status synchroon bijwerken
Over het algemeen is het beter om de state asynchroon bij te werken via navigation.reload({state: newState}) , zodat je "navigate" -listener die state kan toepassen. Soms is de statewijziging echter al volledig doorgevoerd tegen de tijd dat je code erover hoort, bijvoorbeeld wanneer de gebruiker een <details> -element aan- of uitzet of de state van een formulierinvoer wijzigt. In deze gevallen wil je de state mogelijk bijwerken, zodat deze wijzigingen behouden blijven bij herladen en het doorlopen van de pagina. Dit is mogelijk met updateCurrentEntry() :
navigation.updateCurrentEntry({state: newState});
Er is ook een evenement waar u meer kunt horen over deze verandering:
navigation.addEventListener('currententrychange', () => {
console.log(navigation.currentEntry.getState());
});
Maar als je merkt dat je reageert op statuswijzigingen in "currententrychange" , dan splits je mogelijk je code voor statusafhandeling op of dupliceer je deze tussen de "navigate" -gebeurtenis en de "currententrychange" -gebeurtenis, terwijl navigation.reload({state: newState}) je in staat zou stellen dit op één plek af te handelen.
Status versus URL-parameters
Omdat de status een gestructureerd object kan zijn, is het verleidelijk om deze te gebruiken voor alle applicatiestatus. In veel gevallen is het echter beter om die status in de URL op te slaan.
Als je verwacht dat de status behouden blijft wanneer de gebruiker de URL met een andere gebruiker deelt, sla deze dan op in de URL. Anders is het statusobject de betere optie.
Bekijk alle items
De "huidige vermelding" is echter niet alles. De API biedt ook een manier om toegang te krijgen tot de volledige lijst met vermeldingen waar een gebruiker doorheen heeft genavigeerd tijdens het gebruik van uw site, via de functie navigation.entries() , die een momentopname van de vermeldingen retourneert. Dit kan bijvoorbeeld worden gebruikt om een andere gebruikersinterface weer te geven op basis van hoe de gebruiker naar een bepaalde pagina is genavigeerd, of om simpelweg terug te kijken naar eerdere URL's of hun status. Dit is niet mogelijk met de huidige History API.
Je kunt ook luisteren naar een "dispose" gebeurtenis op individuele NavigationHistoryEntry 's. Deze gebeurtenis wordt geactiveerd wanneer de vermelding niet langer deel uitmaakt van de browsergeschiedenis. Dit kan gebeuren als onderdeel van een algemene opruimactie, maar ook tijdens het navigeren. Als je bijvoorbeeld 10 plaatsen teruggaat en vervolgens vooruit navigeert, worden die 10 geschiedenisvermeldingen verwijderd.
Voorbeelden
De "navigate" -gebeurtenis wordt geactiveerd voor alle soorten navigatie, zoals hierboven vermeld. (Er is zelfs een uitgebreide bijlage in de specificatie met een overzicht van alle mogelijke typen.)
Hoewel het meest voorkomende geval voor veel websites is dat de gebruiker op een <a href="..."> klikt, zijn er twee opmerkelijke, complexere navigatietypes die het waard zijn om te bespreken.
Programmatische navigatie
De eerste vorm is programmatische navigatie, waarbij de navigatie wordt veroorzaakt door een methodeaanroep in uw client-side code.
Je kunt vanuit elke plek in je code navigation.navigate('/another_page') aanroepen om een navigatie te starten. Dit wordt afgehandeld door de gecentraliseerde gebeurtenislistener die is geregistreerd bij de "navigate" listener, en je gecentraliseerde listener wordt synchroon aangeroepen.
Dit is bedoeld als een verbeterde samenvoeging van oudere methoden zoals location.assign() en soortgelijke methoden, plus pushState() en replaceState() methoden van de History API.
De methode navigation.navigate() retourneert een object dat twee Promise instanties bevat in { committed, finished } . Dit stelt de aanroeper in staat te wachten totdat de overgang "committed" is (de zichtbare URL is gewijzigd en er is een nieuwe NavigationHistoryEntry beschikbaar) of "finished" (alle promises die door intercept({ handler }) worden geretourneerd, zijn voltooid of afgewezen, vanwege een fout of omdat ze werden onderbroken door een andere navigatie).
De navigate methode heeft ook een optieobject, waarin je het volgende kunt instellen:
-
state: de status van de nieuwe geschiedenisvermelding, zoals beschikbaar via de.getState()-methode op deNavigationHistoryEntry. -
history: dit kan worden ingesteld op"replace"om de huidige geschiedenisvermelding te vervangen. -
info: een object dat vianavigateEvent.infoaan de navigate-gebeurtenis wordt doorgegeven.
info kan bijvoorbeeld nuttig zijn om een specifieke animatie aan te duiden die ervoor zorgt dat de volgende pagina verschijnt. (Een alternatief zou zijn om een globale variabele in te stellen of deze als onderdeel van de #hash op te nemen. Beide opties zijn echter wat onhandig.) Het is belangrijk om te weten dat deze info niet opnieuw wordt gebruikt als een gebruiker later navigeert, bijvoorbeeld via de knoppen Terug en Vooruit. In die gevallen zal de informatie altijd undefined zijn.
navigation heeft ook een aantal andere navigatiemethoden, die allemaal een object retourneren met de waarde { committed, finished } . Ik heb traverseTo() (die een key accepteert die een specifieke vermelding in de gebruikersgeschiedenis aangeeft) en navigate() al genoemd. Het omvat ook back() , forward() en reload() . Deze methoden worden, net als navigate() , afgehandeld door de centrale eventlistener "navigate" .
Formulierinzendingen
Ten tweede is het verzenden van een HTML <form> via POST een speciaal type navigatie, en de Navigation API kan dit onderscheppen. Hoewel het een extra payload bevat, wordt de navigatie nog steeds centraal afgehandeld door de "navigate" -listener.
Het verzenden van een formulier kan worden gedetecteerd door te kijken naar de eigenschap formData in de NavigateEvent . Hier is een voorbeeld dat elke formulierinzending eenvoudigweg omzet in een inzending die op de huidige pagina blijft via 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"
},
});
}
});
Wat ontbreekt er?
Ondanks het gecentraliseerde karakter van de "navigate" -gebeurtenislistener, activeert de huidige Navigation API-specificatie "navigate" niet bij de eerste keer laden van een pagina. Voor sites die Server Side Rendering (SSR) gebruiken voor alle statussen, is dit wellicht geen probleem: uw server kan de juiste initiële status retourneren, wat de snelste manier is om content aan uw gebruikers te leveren. Maar sites die client-side code gebruiken om hun pagina's te genereren, moeten mogelijk een extra functie toevoegen om hun pagina te initialiseren.
Een andere bewuste ontwerpkeuze van de Navigation API is dat deze alleen binnen één frame werkt, oftewel de hoofdpagina of een specifiek <iframe> . Dit heeft een aantal interessante implicaties die verder worden beschreven in de specificatie , maar in de praktijk zal het de verwarring bij ontwikkelaars verminderen. De vorige History API had een aantal verwarrende uitzonderingen, zoals ondersteuning voor frames, en de vernieuwde Navigation API pakt deze uitzonderingen vanaf het begin aan.
Ten slotte bestaat er nog geen consensus over het programmatisch wijzigen of herschikken van de lijst met items waar de gebruiker doorheen heeft genavigeerd. Dit wordt momenteel besproken , maar een mogelijke optie zou zijn om alleen verwijderingen toe te staan: ofwel historische items, ofwel "alle toekomstige items". De laatste optie zou een tijdelijke status mogelijk maken. Als ontwikkelaar zou ik bijvoorbeeld het volgende kunnen doen:
- Stel de gebruiker een vraag door naar een nieuwe URL of status te navigeren.
- De gebruiker de mogelijkheid geven om zijn/haar werk af te maken (of terug te gaan)
- Een geschiedenisitem verwijderen na voltooiing van een taak
Dit zou perfect zijn voor tijdelijke modals of interstitials: de nieuwe URL is iets waar een gebruiker met de Terug-knop vanaf kan gaan, maar vervolgens niet per ongeluk naar voren kan gaan om het opnieuw te openen (omdat het item is verwijderd). Dit is gewoon niet mogelijk met de huidige History API.
Probeer de navigatie-API.
De Navigation API is beschikbaar in Chrome 102 zonder extra vlaggen. Je kunt ook een demo van Domenic Denicola uitproberen .
Hoewel de klassieke History API eenvoudig lijkt, is deze niet erg goed gedefinieerd en kent ze veel problemen met betrekking tot uitzonderlijke gevallen en de verschillende implementaties in diverse browsers. We hopen dat u feedback wilt geven op de nieuwe Navigation API.
Referenties
- WICG/navigatie-api
- Standpunt van Mozilla over standaarden
- Intentie om te prototypen
- TAG-recensie
- Chromestatus-item
Dankbetuigingen
Met dank aan Thomas Steiner , Domenic Denicola en Nate Chapin voor het nakijken van dit bericht.