O webu
Změna URL a AJAX

Pokud se jednotlivé stránky webové aplikace mají načítat asynchronně (AJAXem), je důležité spolu se změnou obsahu změnit i URL. Proč?

  • Aby se dalo na stránku odkázat,
  • aby se návštěvník mohl pohybovat historií prohlížeče,
  • aby danou URL mohl vyhledávač zaindexovat.

Kromě změny URL je potřeba zároveň zajistit, aby se po zadání konkrétní URL zpětně načetl příslušný obsah.

Změna URL

Od IE 10 lze napříč prohlížeči používat history.pushState:

if (history.pushState) { // Test podpory
  // Načtení obsahu AJAXem
  window.history.pushState(objektObsah, "Titulek stránky", "url-stranky");
}

Parametry Titulek a URL jsou jasné. Parametr objektObsah slouží k uchování dat právě pro případ pohybu v historii prohlížeče. Jedná se o běžný JS objekt:

window.history.pushState(
  {nadpis: "Text nadpisu", obsah: "Obsah stránky"}, 
  "Titulek stránky", 
  "url-stranky"
);

Ideální proto je do něj uložit obsah stránky, který se mění (nadpis, obsah, …). K uloženým hodnotám objektu se dá dostat z history.state. Zjistit, že se má načíst obsah z history.state, je možné z události window.onpopstate. Testovací ukázka.

window.onpopstate = function(event) {
  alert(event.state.nadpis);
};

history.replaceState

Kromě pushState existuje ještě metoda replaceState, která funguje víceméně stejně, jen místo nové položky v historii nahradí tu současnou.

Změna #hashe ve starých prohlížečích

V prohlížečích IE 9 a starších je jediná možnost, jak zajistit změnu URL u AJAXem načítaného obsahu, měnit hodnotu location.hash.

Zachytit změnu hashe jde od IE 8 přes window.onhashchange; pro ještě starší prohlížeče nezbývá než location.hash kontrolovat časovačem.

Odkazování

Problém tohoto řešení je v tom, že URL v obvyklém tvaru http://example.com/#!url-stranky může být dostupná jen při zapnutém JS.

Vyhledávače a další služby (typu facebookové načítání titulku a popisu stránky u odkazů) to řeší tak, že adresu obsahující tzv. hashbang (#!) – tj. například http://example.com/#!url-stranky přepíší na http://example.com/?_escaped_fragment_=url-stranky a tam očekávají obsah.

Nešťastné je, že takto nefungují zdaleka všechny služby a návštěvníci bez JavaScriptu se na URL s fragmentem/hashem nedostanou.

Další nevýhoda je, že fragment v URL neumí serverový skript spolehlivě zachytit, takže při příchodu na example.com/#!url-stranky se stejně nejprve vygeneruje stránka example.com, aby se potom skriptem stáhla ještě example.com/url-stranky. Očekávané zrychlení webu je potom fuč.

Při použítí history.pushState tyto problémy nehrozí. JavaScript mění URL stránek na plnohodnotné, takže se po příchodu na tuto URL načte obsah standardně ze serveru a asynchronně se donačítá až další obsah při procházení. Vyhledávače a roboti používají jen obsah přímo ze serveru.

Co používat?

Použití history.pushState s plnohodnotnými adresami se zdá být docela rozumné (už to tak funguje mj. na YouTube). Měnit adresu za #! už je trochu slabší, ale při správném řešení to jako náhrada pushState může sloužit, tj.:

  • Všude používat plnohodnotné adresy (cíl/href odkazů na #! variantu měnit v daných prohlížečích až skriptem).
  • Zajistit zobrazení příslušného obsahu na URL s ?_escaped_fragment_=, aby např. návštěvník v IE 9 mohl stránku normálně nasdílet na Facebooku.
  • Zajistit, aby se po zadání adresy s fragmentem zobrazil odpovídající obsah. V prohlížečích, co umí pushState, fragment odstraňovat.

Pořád ale přetrvává nevýhoda dvojitého načítání a získávání nehezkých zpětných odkazů s #!.

Reset a zrychlení

Kromě toho je dobré zvážit, zda není při běžném (neajaxovém) procházení webů výhodné to, že se při každém načtení stránky všechen JavaScript v podstatě vyresetuje. Hrozí tak menší risiko, že se něco rozbije a návštěvník neznalý reload tlačítka nebude na stránce schopen dalšího fungování.

Nakonec u rychlého webu nebude nejspíš zisk z AJAXového načítání jen části stránky moc významný.

Odkazy jinam