pátek 4. května 2012

Změna url webu pomocí javascriptu od nerozeznání od webu bez js

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">
    &copy; 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");

Odkazy

Žádné komentáře:

Okomentovat