En simpel introduktion til History API i JavaScript
History API er en af disse JavaScript-teknikker, som enhver internetudvikler har brug for at vide koldt. Uden det, vil et enkelt klik på knappen Tilbage hoppe lige ud af din ansøgning. Du mister ethvert igangværende arbejde, og brugeren bliver tvunget til at starte igen.
heldigvis er der en nem løsning. Lige siden formaliseringen af HTML5 (og udgivelsen af Internetudforsker 10) har vi haft en historie API, der lader dig føje til historielisten og reagere på frem og tilbage navigation. I denne artikel lærer du, hvordan det fungerer — og du vil prøve det med et levende eksempel.
men inden vi går videre, lad os se nærmere på problemet.
i de gamle dage af internettet gjorde en hjemmeside en ting—vise ting. Når du ville se på noget andet, klikkede du på et link og gik til en ny URL.
da JavaScript blev mere kraftfuld, indså udviklere, at hver hjemmeside kunne være en komplet applikation i sig selv. En hjemmeside kan konkurrere med en desktop applikation! Problemet var, at knapperne tilbage og fremad i bro.ser ikke passede til denne nye applikationsmodel. En bruger kan f.eks. udføre en række opgaver i et enkeltsidet internetprogram og derefter forvente at bruge knappen Tilbage til at gå et enkelt trin tilbage. I stedet, tilbage-knappen ville vende tilbage til den forrige hjemmeside, effektivt lukke JavaScript app i midten af hvad det gjorde.
ofte var denne adfærd ikke intuitiv. Et program, der brugerXMLHttpRequest
– objektet til at hente nye data og opdatere siden. For brugeren kan det se ud som om de rejser til en ny side. Men hvis de begår fejlen ved at bruge tilbage-knappen, er illusionen knust, og alt går sidelæns.
lad os se nærmere på. Her er et eksempel, der lader dig gå gennem Dictionnaire Infernal, en berømt (og nu offentlig ejendom) bog fra 1818, der beskriver mærkelige dyr fra overtro og mytologi. Tricket er, at de forrige og næste links ikke fører dig til en anden side. I stedet udløser de et asynkront opkald gennem XMLHttpRequest
, der får oplysningerne til det næste dias. Derefter opdaterer JavaScript-koden diasboksen uden at forstyrre indholdet på resten af siden.
Her er en live version af eksemplet, komplet med historie API support. Selvom alt foregår på en enkelt hjemmeside, kan du bruge knapperne frem og tilbage til problemfrit at gå fra et billede til det næste. Du kan endda bruge genindlæsningsknappen (genindlæser det aktuelle dias i stedet for at genstarte hele appen), og du kan placere bogmærker, der holder den aktuelle tilstand — med andre ord fører de dig lige tilbage til det dias, du i øjeblikket ser på.
og her er en old-school version af eksemplet, der ikke bruger historie API. Det betyder ikke noget, hvor langt du er i sideprogrammet — når du bruger tilbage-knappen, springer du tilbage til den forrige hjemmeside. Knapperne fremad og genindlæs er på samme måde brudt. Bogmærker genstart appen fra begyndelsen.
Du kan hente alle kildekoden her for at spille med. (Det fungerer dog ikke lokalt, fordi det bruger XMLHttpRequest
– objektet til at foretage internetanmodninger.) I resten af denne artikel vil jeg nedbryde, hvordan alt fungerer.
forberedelse af eksemplet
før vi kommer ind i History API, er det vigtigt at have en grundlæggende forståelse af, hvordan dette eksempel fungerer. Det hele er ret simpelt.
siden bruger et tal til at holde styr på det dias, det aktuelt viser. Når du klikker på Næste eller forrige, ændrer koden det aktuelle diasnummer. Derefter bruger den sin egen goToSlide()
funktion til at opdatere siden.
goToSlide()
funktionen er, hvor det virkelige arbejde finder sted. Det starter en asychronous anmodning ved hjælp af den berygtede XMLHttpRequest
objekt.
det er op til dig at bestemme, hvordan diasindholdet genereres og sendes tilbage til opkaldskoden på siden. Normalt har du en slags routingramme, der udløser den rigtige server-side kode. Denne server-side-kode kan derefter få fat i diasindholdet fra en cache, en database, en fil eller endda en hårdkodet blok af markering.
i dette eksempel har jeg holdt tingene så enkle som muligt. Anmodningen griber HTML-markeringen fra en statisk fil. Så hvis du anmoder om det tredje dias (med en relativ URL som Dias/3), får du et uddrag af HTML-markering for netop det dias. Rolig!
Når anmodningen er færdig, udløser bro.sernewSlideReceived()
event handleren. Så er det simpelthen et spørgsmål om at tage den modtagne markering og indsætte den på siden ved hjælp af en pladsholder <div>
.
det er alt hvad du behøver at vide for at opbygge den naive version af denne side, som ikke understøtter bro.serhistorik.
historikobjektet
history
objektet har eksisteret næsten siden begyndelsen af JavaScript. Men i store dele af sit liv var det langt mindre magtfuldt (og langt mindre nyttigt) end det er i dag.
originalenhistory
objektet har kun en egenskab og tre grundlæggende metoder. Ejendommen er length
, og den fortæller dig, hvor mange poster der er i Bro.sererens historieliste:
alert("You have " + history.length + " pages in the history.");
denne detalje er for det meste ubrugelig.
de tre originale history
metoder er back()
(sender en besøgende et skridt tilbage i gennemsynshistorikken), forward()
(sender en besøgende et skridt fremad) og go()
(tager en besøgende et skridt tilbage i gennemsynshistorikken), forward()
(sender en besøgende et skridt fremad) og go()
(tager en numerisk forskydning, der fortæller bro.ser, hvor mange trin der skal gå frem eller tilbage). Alt dette tilføjer relativt lidt, medmindre du ønsker at designe dine egne brugerdefinerede frem og tilbage knapper på en hjemmeside.
Historienapi tilføjer to meget mere nyttige ingredienser:pushState()
metode ogonPopState
begivenhed.
tilføjelse af en post til historielisten
pushState()
metode er midtpunktet i Historie API. Det giver dig mulighed for at tilføje et nyt element til historielisten for den aktuelle side. Sådan ser det ud:
på dette tidspunkt undrer du dig måske — hvad er meningen med at have mere end en post til den samme side? Tricket er, at hver post har en eller anden tilstand knyttet til det — et linket stykke information eller objekt, som du indstiller. Når brugeren træder tilbage gennem historielisten, får du de tilsvarende tilstandsoplysninger, så du kan returnere siden til dens tidligere version.
pushState()
metoden tager tre argumenter:
- Data. Det første argument er ethvert stykke data, du vil gemme for at repræsentere den aktuelle tilstand på denne side. Du bruger disse oplysninger til at gendanne siden, når brugeren går tilbage gennem historiklisten.
- Titel. Det andet argument er den sidetitel, du vil have, at bro.sereren skal vise. I øjeblikket er alle bro. sere forenet i at ignorere denne detalje. (Så du kan bare passere
null
.) - URL. Det tredje argument er den nye version af den URL, du vil vise for siden i adresselinjen. Dette giver dig mulighed for at tilføje bedre understøttelse af genindlæsningsknappen og bro.ser-bogmærker.
i Dictionnaire Infernal diaseksempel er det nemt at tilføje historikstøtte alt hvad du behøver for at holde styr på er diasnummeret. Du føjer til oversigtslisten, hver gang diaset ændres.
Her er den kodelinje, du skal tilføje:
history.pushState(slideNumber, null, null);
du vil bemærke, at denne kode ikke ændrer sidetitlen med det andet argument (fordi det ikke virker alligevel) og ændrer ikke URL ‘ en (du kan se, hvordan du gør det om et øjeblik). Alt det gør er at gemme diasnummeret.
for det fulde billede er her de to opkald til history.pushState()
på deres rette sted — begivenhedshåndtererne til de næste og Forrige links:
vender tilbage til en forrige side
Når du brugerpushState()
metode, skal du også tænke på dens naturlige modstykke,onPopState
begivenhed. MenspushState()
– metoden sætter en ny post i Bro.sererens historieliste, giveronPopState
– begivenheden dig chancen for at håndtere det, når brugeren vender tilbage.
for at forstå, hvordan dette fungerer, skal du overveje, hvad der sker, hvis en besøgende træder gennem alle lysbillederne i Dictionnaire Infernal-eksemplet. Når hvert nyt dias indlæses, tilføjer siden en historikpost. Men hvis brugeren træder tilbage med tilbage-knappen, sker der intet.
for at løse denne mangel skal du håndtere onPopState
begivenhed, som udløses efter hver historiknavigation. (Dette inkluderer, hvis du bruger en af historikmetoderne, som history.back()
, samt når brugeren klikker på navigationsknapperne.)
onPopState
begivenheden giver din kode de tilstandsoplysninger, du har gemt tidligere medpushState()
. I dette tilfælde er det bare diasnummeret, som du kan få fat i fra state
egenskaben for begivenhedsargumentet, som sådan:
var slideNumber = e.State;
Dit job er at bruge tilstandsoplysningerne til at gendanne den korrekte version af siden. I det aktuelle eksempel betyder det at indlæse det tilsvarende dias ved at kalde den praktiske goToSlide()
– funktion, der håndterer al diasnavigation. Den komplette kode er kort og ligetil:
Bemærk, at du skal kontrollere, ome.State
ernull
, hvilket sker på nogle bro.sere, når siden indlæses for første gang, og der er ingen gemt tilstand.
ændring af URL
den aktuelle version af dette eksempel understøtter historielisten, men spiller ikke pænt med genindlæsningsknappen. Hvis du f.eks. klikker videre til det tredje dias og derefter opdaterer siden, ender du tilbage i begyndelsen. Du finder dig selv i samme situation, hvis du forsøger at bogmærke et af diasene — vend tilbage til bogmærket, og du starter tilbage på det første dias.
løsningen på disse problemer er at bruge History API til at ændre URL ‘ en. Men der er en vigtig grund til, at jeg har forladt dette trin indtil sidst. Ofte ændrer eksempler, der bruger History API, URL ‘ en, men bruger ikke den nye URL. Dette kan føre til en internetapp, der går i stykker, når brugeren opdaterer den (med en grim 404-fejl) eller går til en lignende, men lidt anden version af siden. Hvis du ikke er sikker på, hvordan du håndterer forskellige URL ‘ er, er der ingen skam i at bruge den forenklede tilgang fra det foregående eksempel.
så lad os sige, at du er klar til at dykke ind, lave bedre URL ‘ er og spille pænt med bogmærker og opdateringer. Der er to grundlæggende tilgange, du kan tage:
- Skift URL-sidenavnet helt. I Dictionnaire Infernal eksempel kan du ændre URL ‘ en til Slide1.html, Slide2.html, og så videre som brugeren bevæger sig gennem dias. Fangsten er, at du enten skal have faktiske sider med disse navne (gør det ikke, det er besværligt), eller du skal konfigurere routing, så når din internetserver får en anmodning om Slide2.html det kører kode, der skaber den rigtige version af siden.
- behold det samme sidenavn, men tilføj oplysninger til forespørgselsstrengen. Dette er en enklere, mindre tilgang. Når brugeren rejser gennem diasvisningen, ender du med URL ‘ er som diasvisning.html?slide=1, og derefter Slide.html?slide=2.html?slide = 3 osv. Fordelen ved denne tilgang er, at det er trivielt nemt at skrive et lille stykke JavaScript, der kontrollerer forespørgselsstrengen og sørger for, at du er på det rigtige dias, når siden genindlæses. Du har ikke brug for nogen serverkode for at få dette til at fungere — en side kan gøre det hele.
Her er en ændret version afpushState()
opkald, du så tidligere, der bruger den anden tilgang. Denne kode fylder diasnummeret i slutningen af URL ‘ en:
history.pushState(slide, null, "Dictionnaire.html?slide=" + slide);
og her er koden, der kører, når siden genindlæses. Den kontrollerer for diasoplysningerne i URL ‘ en, og — hvis den finder et korrekt nummer — kalder goToSlide()
– funktionen for at indlæse diaset.
nu mister du aldrig din plads i diveskuespillet. Du kan prøve den komplette version af dette eksempel eller hente alt her.
endelige detaljer
historik API er nem at bruge, men det gør det også nemt at skabe større problemer, som du løser. Nu hvor du ved, hvordan alt passer sammen, her er nogle sidste råd:
- hvis du skubber, skal du pop.
pushState()
metode ogonPopState
begivenhed er partnere. Hvad du lægger i med en, kommer du tilbage med den anden. - Du må ikke ændre URL ‘ en, medmindre du er klar til at håndtere den. Det er rart at ændre din URL for at afspejle din skiftende side, men du skal være i stand til at håndtere anmodninger om hver URL, du bruger. Hvis din URL peger på en side, der ikke findes, går din applikation i stykker på opdateringer og bogmærker.
- brug et brugerdefineret tilstandsobjekt til at gemme flere oplysninger. Du behøver ikke at holde fast i et enkelt nummer. Du kan sætte et helt brugerdefineret objekt i tilstand, som kan samle alle de oplysninger, du har brug for til en kompleks opgave.