使用HTML5的History API

  HTML5 History API提供了一種功能,能讓開發人員在不刷新整個頁面的狀況下修改站點的URL。這個功能頗有用,例如經過一段JavaScript代碼局部加載頁面的內容,你但願經過改變當前頁面的URL來反應出頁面內容的變化,這時該功能能夠派上用場。css

  舉個例子,當用戶從首頁進入幫助頁面時,咱們經過Ajax來加載幫助頁面的內容。而後這個用戶又轉到產品頁面,咱們須要再一次經過Ajax請求來替換頁面的內容。當用戶想分享頁面的URL時,經過History API,咱們能夠改變頁面的URL來反應內容的修改,這樣不論是用戶分享仍是保存的URL都能和頁面的內容對應起來。html

基本知識

  要查看這個API提供了哪些功能很是簡單,打開瀏覽器的Developer Tools工具面板,而後在console中輸入history。若是你的瀏覽器支持History API,你將會看到這個對象下面附帶了不少方法。html5

  注意其中的pushStatereplaceState這兩個方法。咱們能夠在console中進行一些簡單的測試,來看看當咱們使用這兩個方法時URL會發生什麼變化。稍後咱們將分析這兩個方法中的全部參數,如今咱們只須要關注最後一個參數:jquery

history.replaceState(null, null, 'hello');

  上面代碼中的replaceState方法改變了當前頁面的URL,在後面添加了一個'/hello'。不過並無發出任何request請求,當前窗口仍然停留在以前的頁面。不過這裏有個問題,當你點擊瀏覽器的後退按鈕時,頁面並不會回退到咱們經過replaceState方法修改以前的那個URL,而是直接回退到了上一個頁面(即咱們進入到這個頁面以前的那個頁面)。這是由於replaceState方法不會修改瀏覽器的history,它只是簡單地替換了地址欄中的URL。git

  要解決這個問題咱們須要使用pushState方法:github

history.pushState(null, null, 'hello');

  如今再點擊瀏覽器的後退按鈕,你會發現它和你預想的效果同樣。由於pushState方法將咱們傳給它的URL添加到瀏覽器的history中,從而改變了瀏覽器的history。假如咱們將另一個完整的站點URL傳遞給它會發生什麼狀況呢?例如咱們在baidu.com的首頁進行測試,而後在console中輸入下面的內容。web

history.pushState(null, null, 'https://twitter.com/hello');

  瀏覽器會報錯。由於傳遞給pushState方法的URl必須和當前頁面的URL屬於同一個源(即不能跨域),不然會有很大的安全漏洞,開發人員可能會借用該功能來欺騙用戶,讓他們以爲本身是在訪問一個徹底不一樣的站點,而事實並不是如此。api

  來看看傳遞給pushState方法的全部參數:跨域

history.pushState([data], [title], [url]);
  1. 第一個參數用來傳遞咱們須要的數據,當頁面的狀態發生變化時咱們能夠接收到該數據。如用戶點擊瀏覽器的後退和向前按鈕。須要注意的是在Firefox中只容許傳遞最多640K的數據。
  2. 第二個參數title是一個字符串,不過截止到目前,幾乎全部的瀏覽器都忽略該參數。
  3. 最後一個參數是咱們想要替換的URL。

簡單回顧一下

  這些History API最主要的功能就是不從新加載頁面。以往咱們只能經過改變window.location的值來修改當前頁面的URL,不過這會致使整個頁面被從新加載。若是你修改的只是URL中的hash,則不會致使頁面被刷新。 瀏覽器

  使用舊的hashbang方法能夠改變頁面的URL而不刷新頁面。著名的Twitter就是使用的該方法,不過也廣受詬病,畢竟hash在location中並不被做爲一個真正的資源來對待。

  做爲History API的早期支持者,Twitter後來拋棄了傳統的hashbang方法。在2012年,Twitter的團隊介紹了他們的新方法,並列出了其中的一些問題同時還詳細地介紹了各瀏覽器應該如何實現該規範。

一個使用pushState和Ajax的例子

https://css-tricks.com/examples/State/

  在該示例中,咱們但願用戶經過咱們的網站找到電影捉鬼敢死隊(一部美國電影)中的演員。當用戶選擇一個圖片時,咱們須要在下方顯示該演員對應的文字描述,同時給該圖片一個被選中的效果。當點擊後退按鈕時,頁面應該切換到上一個被選中的圖片狀態,同時圖片下方的文字也要一併切換。當點擊前進按鈕時也同樣。

  這裏有一個效果圖:

  這個示例的HTML代碼很是簡單:div.gallery中包含了全部的連接,每一個連接裏有一個圖片。接下來咱們放置了一個空的div.content,用來存放當演員圖片被點擊時顯示在下放的文字。

<div class="gallery">
  <a href="https://cdn.css-tricks.com/peter.html">
    <img src="bill.png" alt="Peter" class="peter" data-name="peter">
  </a>
  <a href="https://cdn.css-tricks.com/ray.html">
    <img src="ray.png" alt="Ray" class="ray" data-name="ray">
  </a>
  <a href="https://cdn.css-tricks.com/egon.html">
    <img src="egon.png" alt="Egon" class="egon" data-name="egon">
  </a>
  <a href="https://cdn.css-tricks.com/winston.html">
    <img src="winston.png" alt="Winston" class="winston" data-name="winston">
  </a>
</div>

<p class="selected">Ghostbusters</p>
<p class="highlight"></p>

<div class="content"></div>

  若是沒有JavaScript該頁面仍然能夠正常工做,點擊圖片能夠跳轉到對應的頁面,而後點擊後退按鈕也能夠回到以前的頁面。這是爲了考慮頁面的可訪問行和優雅降級。

  接下來咱們要添加JavaScript代碼了。咱們經過event propagation給div.gallery元素中的每個link添加一個事件處理程序,像這樣:

var container = document.querySelector('.gallery');

container.addEventListener('click', function(e) {
  if (e.target != e.currentTarget) {
    e.preventDefault();
    // e.target is the image inside the link we just clicked.
  }
  e.stopPropagation();
}, false);

  在if語句中,咱們獲取到被選中圖片的data-name屬性的值,而後將'.html'添加到後面拼成一個要訪問的頁面地址,並將其做爲第三個參數傳遞給pushState方法(不過在真實的例子中咱們可能會在Ajax請求成功以後纔會去修改URL)。

var data = e.target.getAttribute('data-name'),
url = data + ".html";
history.pushState(null, null, url);
    
// 此處更改當前的classes樣式
// 而後使用data變量的值更新
// 並經過Ajax請求.content元素的內容
// 最後再更新當前文檔的title

(固然,此處咱們也能夠直接使用link的href屬性的值)

  我將真實代碼中的內容都替換成註釋了,這樣咱們能夠只關注pushState方法的使用。

  如今咱們點擊圖片,URL和Ajax請求的內容會被自動更新,可是當咱們點擊後退按鈕時並不會回退到以前選中的演員圖片。這裏咱們還須要在用戶點擊後退和前進按鈕時使用另一個Ajax請求來更新內容,並再一次使用pushState方法來更新頁面的URL。

  咱們使用pushState方法中的第一個參數(其中的state)來保存狀態信息:

history.pushState(data, null, url);

  上面代碼中的data參數在popstate事件觸發時能夠被獲取到。當瀏覽器的後退和前進按鈕被點擊時會觸發popstate事件。

window.addEventListener('popstate', function(e) {
  // e.state表示上一個被點擊的圖片的data-attribute
});

  咱們能夠經過該參數傳遞一些咱們須要的信息,例如在該示例中咱們將以前選中的捉鬼敢死隊的演員做爲參數傳遞給requestContent方法,在該方法中,咱們使用jQuery的load方法進行一次Ajax請求。

function requestContent(file) {
  $('.content').load(file + ' .content');
}

window.addEventListener('popstate', function(e) {
  var character = e.state;

  if (character == null) {
    removeCurrentClass();
    textWrapper.innerHTML = " ";
    content.innerHTML = " ";
    document.title = defaultTitle;
  } else {
      updateText(character);
      requestContent(character + ".html");
      addCurrentClass(character);
      document.title = "Ghostbuster | " + character;
  }
});

  若是用戶點擊了演員Ray的圖片,event listener會被觸發,而後在pushState事件中保存圖片的data屬性的值。當用戶點擊另一個圖片,並點擊了瀏覽器的後退按鈕,此時popstate事件會被觸發,從而從新加載ray.html頁面。

  這意味着什麼呢?當咱們點擊一個演員的圖片而後將被更改的URL分享出去,用戶訪問這個URL時對應的HTML文件會被自動加載進來。這會帶來一些更好的用戶體驗,並保證了URL和頁面內容的一致性從而減小了所以而帶給用戶的一些困惑。

  上面的示例只是簡單地經過jQuery來動態加載內容,咱們固然也能夠在pushState方法中傳遞一些更加複雜的對象。不過這個例子已經能足夠說明問題並幫助咱們開始學習如何使用History API的功能。咱們先要學會走,而後才能跑。

下一步

  若是咱們想大範圍地使用這種技術,咱們應該考慮使用一些專有的工具,例如pjax 它是一個jQuery的插件,使用它能夠大大提升咱們同時使用Ajax和pushState方法進行開發的速度,不過它只支持那些使用History API接口的現代瀏覽器。

  History JS能夠兼容舊瀏覽器,對於不支持History API接口的瀏覽器,它依然使用舊的URL hash的方式來實現一樣的功能。

有關URLs

  這裏我特別引用了Kyle Neath有關URLs的說明:

URLs是一個通用的概念,它能夠工做在Firefox, Chrome, Safari, Internet Explorer, curl, wget,甚至在你的iPhone, Android以及便籤紙上。它是web中的一個通用的語法。不要認爲這是理所固然的。任何一個稍微懂點技術的用戶均可以瀏覽你的應用的90%以上的部分而不用去刻意記住那些URL的結構。要實現這樣的效果,你須要考慮URLs的實用性。

  這意味着不論你想要進行什麼樣的hacks或性能優化,做爲web開發人員,你應該注重URL。而隨着HTML5 History API的幫助,咱們能夠輕鬆地解決諸如上述示例中的一些問題。

常見問題

  • 將Ajax請求的地址嵌入到a標記的href屬性中一般是個不錯的主意。
  • 確保在JavaScript的click事件處理程序中return true,這樣當有人使用中鍵點擊或命令點擊時不會致使程序被意外覆蓋。

補充

瀏覽器支持

Chrome Safari Firefox Opera IE Android iOS
31+ 7.1+ 34+ 11.50+ 10+ 4.3+ 7.1+

原文地址:https://css-tricks.com/using-the-html5-history-api/

相關文章
相關標籤/搜索