Změna url webu pomocí javascriptu od nerozeznání od webu bez js
Úvod
HTML5 umožňuje změnu url v rámci domény. To dříve nešlo a obcházelo se to většinou pomocí url za hashmarkem (#), protože to je část, která se mění jen v rámci stránky, ale neposílá se na server.
A k čemu to vlastně bude? Touto metodou se stránka může chovat jako by staticky přepínala stránky a přitom bude stránku ovládat javascript a dotahovat stránku na pozadí, samozřejmě s fallbackem pokud prohlížeč nebude pushState podporovat nebo bude mít vyplý javascript. Chování je pro uživatele opticky stejné, ale z programátorského hlediska může být tento způsob výhodnější - žádný refresh.
Volání je jednoduché, stačí zavolat metodu pushState objektu history:
history.pushState(objekt, nazev, url);
Metoda mimojiné kromě změny url (která je nepovinná) mění "stav" aplikace nastavením aktuálního stavového objektu a jeho názvu. Aktuální stavový objekt je uložen v proměnné
history.state
Nám ale bude stačit fukčnost změny url. Více informací o stavech historie je možné se dočíst ve specifikaci
Struktura stránky
Tady je příklad jak může vypadat stránka, u které chceme použít navigace:
<!DOCTYPE html> <html lang="cs"> <head> <meta charset="utf-8"> <title>titulek</title> </head> <body> <header id="header"> <h1>hlavni nadpis</h1> </header> <nav id="nav"> <ul> <li class="vybrany"><a href="uvod.html">Úvod</a></li> <li><a href="zivotopis.html">Životopis</a></li> <li><a href="projekty.html">Projekty</a></li> <li><a href="dalsi.html">Další záliby</a></li> </ul> </nav> <section id="content"> <h2>Úvod</h2> <p>Now that there is the Tec-9, a crappy spray gun from South Miami. This gun is advertised as the most popular gun in American crime. Do you believe that shit? It actually says that in the little book that comes with it: the most popular gun in American crime. Like they're actually proud of that shit. </p> </section> <footer id="footer"> © 2012 <a href="mailto:#">Karel Hovorka</a> </footer> </body> </html>
Navázání události na kliknutí
Kromě samotného javascriptu si pomůžeme knihovnou jQuery. Chceme odchytit klik na každý odkaz v elementu #nav, toho docílíme následujícím:
$(aSelector).each (function() { $(this).bind("click", function (e) { //TODO: klik }); });
Výkonný kód musí dotáhnout data pomocí zvoleného odkazu a aktualizovat potřebná data je potřeba nějakým způsobem roztřídit, na to zvolíme css selektory - my budeme chtít aktualizovat hlavní nadpis (#header h1), obsah (#content) a titulek (title).
Ajax a parsování dat
Parsováná data se napřed zkrátí jen na část uvnitř body a obsah elementu title, aby se zbytečně neimportovali a nespouštěli css nebo skripty (skripty uvnitř elementu body se budou pouštět). Volání ajax requestu a první parsování dat vypadá následovně:
var request = $.get(href, function(data) { data = data.substring(data.indexOf("<body>") + 6, data.lastIndexOf("</body>")); var title = data.substring(data.indexOf("<title>") + 7, data.lastIndexOf("</title>")); var bodyDom = $($('<div></div>').append(data)); //TODO: aktualizovat data window.history.pushState("object" + href, "title" + href, href); });
Aktualizace dat
Po poslední části kódu už je k dispozici DOM obsahu dotáhlého dokumentu a obsah elementu title, teď akorát chybí data aktualizovat:
var contentSelectors = ["#header h1", "#content", "title"]; var selector; for (i = 0; i < contentSelectors.length; i++) { selector = contentSelectors[i]; if (selector == "title") { document.title = title; } else { $(selector).html($(selector, bodyDom).html()); } }
Aktualizace odkazů
Poslední věc, která se často ještě mění je třída položky seznamu, která je zrovna vybrána, toto se aktualizuje následujícím kódem, který napřed třídu vymaže všem existujícím a následně nastaví aktuálním:
var clazz; $("." + clazz).each (function() { $(this).removeClass(clazz); }); $("a[href='" + href + "']").each (function() { $(this).parent("li").addClass(clazz); });
Toto jsou snad všechny základní myšlenky. Ještě se musí na začátku zjistit jestli prohlížeč vůbec pushState podporuje a navázat události click jen pokud ano. Pak se také musí kontrolovat, že klik byl levým tlačítkem. Nasazení naostro můžete vidět na karel.hovorka.net.
Výsledný kód
Výsledný kód po úpravách a dekompozici:
function HTML5Nav(contentSelectors, clazz) { this.clazz = clazz; this.contentSelectors = contentSelectors; } HTML5Nav.prototype.load = function (aSelector) { var globalThis = this; if (window.history.pushState) { $(aSelector).each (function() { $(this).bind("click", function (e) { globalThis._click(e); }); }); } } HTML5Nav.prototype._click = function(e) { if (e.which == 1) { var a = $(e.target); var href = a.attr("href"); this.changePage(href); e.preventDefault(); } } HTML5Nav.prototype.changePage = function(href) { var globalThis = this; var request = $.get(href, function(data) { data = data.substring(data.indexOf("<body>") + 6, data.lastIndexOf("</body>")); var title = data.substring(data.indexOf("<title>") + 7, data.lastIndexOf("</title>")); var bodyDom = $($('<div></div>').append(data)); globalThis._ajaxResponse(href, bodyDom, title); }); } HTML5Nav.prototype._ajaxResponse = function(href, bodyDom, title) { var selector; for (i = 0; i < this.contentSelectors.length; i++) { selector = this.contentSelectors[i]; if (selector == "title") { document.title = title; } else { $(selector).html($(selector, bodyDom).html()); } } window.history.pushState("object" + href, "title" + href, href); this._setSelection(href, this.clazz); } HTML5Nav.prototype._setSelection = function(href) { var clazz = this.clazz; $("." + clazz).each (function() { $(this).removeClass(clazz); }); $("a[href='" + href + "']").each (function() { $(this).parent("li").addClass(clazz); }); }
Příklad volání
První parametr jsou selektory elementů, které se mají aktualizovat, druhý parametr je třída, kterou mají mít položky seznamu vybraného odkazu. Metoda load naváže na odkazy událost kliku, jako parametr bere selektor na odkazy, na které chcem vázat odkazy. Příklad volání kódu :
var nav = new HTML5Nav(["#header h1", "#content", "title"], "vybrany"); window.onload = nav.load("#nav a");