有關html5的history api

從Ajax翻頁的問題提及

請想象你正在看一個視頻下面的評論,在翻到十幾頁的時候,你發現一個寫得稍長,但很是有趣的評論。正當你想要停下滾輪細看的時候,手殘按到了F5。而後,頁面刷新了,評論又回到了第一頁,因此你又要從新翻一次。javascript

再或者,你想把這個評論發給別人分享,一面給了別人頁面地址(爲何不直接複製呢?由於要連帶視頻等場景啊),一面又要加一句囑咐:請翻到下面評論的第XX頁的XX樓。html

這就是問題。試想一下,若是瀏覽器能記住你當前的狀態(好比看到了第十幾頁),而不是一刷新就還原,是否是就顯得智能多了?前端

爲何用Ajax?

用Ajax實現翻頁等內容切換是有緣由的。在傳統的無Ajax的站點裏,頁面A和頁面B可能只有10%的地方是不一樣的,其餘90%的內容(尤爲是導航、頁腳等公用元素)都是同樣的,但卻仍然須要瀏覽器下載並顯示新的一整個頁面。而若是使用Ajax,不只節省了瀏覽器須要下載的資源,並且無刷新切換明顯比頁面跳轉更平滑、流暢。html5

就視頻下面的評論來講,Ajax能夠說是必須的。視頻這樣的重量級元素,動不動給你從新加載一次,不能忍。java

傳統的跳轉翻頁的可取之處

傳統的不使用Ajax的站點,每個翻頁是一個跳轉,而後你能夠在瀏覽器地址欄裏看到諸如?page=2這樣的參數。每一頁就這樣經過地址欄的URL作了標記,每一次請求,瀏覽器都會根據參數返回正確的頁碼。git

因此,傳統的跳轉翻頁,刷新也不會丟失狀態。github

結合二者

如今咱們就能夠想到,若是在Ajax更新頁面局部內容的同時,也在地址欄的URL裏更新狀態參數,就能夠作出更完美的Ajax翻頁了。後端

不過,JavaScript修改location的除hash外的任意屬性,頁面都會以新URL從新加載。而惟一不引起刷新的hash參數並不會發送到服務器,所以服務器沒法得到狀態。api

而後,HTML5 history API將解決這個問題。瀏覽器

介紹HTML5 history API

HTML5 history API只包括2個方法:history.pushState()history.replaceState(),以及1個事件:window.onpopstate

history.pushState()

它的徹底體是 history.pushState(stateObject, title, url),包括三個參數。

第1個參數是狀態對象,它能夠理解爲一個拿來存儲自定義數據的元素。它和同時做爲參數的url會關聯在一塊兒。

第2個參數是標題,是一個字符串,目前各種瀏覽器都會忽略它(之後纔有可能啓用,用做頁面標題),因此設置成什麼都不要緊。目前建議設置爲空字符串。

第3個參數是URL地址,通常會是簡單的?page=2這樣的參數風格的相對路徑,它會自動以當前URL爲基準。須要注意的是,本參數URL須要和當前頁面URL同源,不然會拋出錯誤。

調用pushState()方法將新生成一條歷史記錄,方便用瀏覽器的「後退」和「前進」來導航(「後退」但是至關經常使用的按鈕)。另外,從URL的同源策略能夠看出,HTML5 history API的出發點是很明確的,就是讓無跳轉的單站點也能夠將它的各個狀態保存爲瀏覽器的多條歷史記錄。當經過歷史記錄從新加載站點時,站點能夠直接加載到對應的狀態。

history.replaceState()

它和history.pushState()方法基本相同,區別只有一點,history.replaceState()不會新生成歷史記錄,而是將當前歷史記錄替換掉。

window.onpopstate

push的對立就是pop,能夠猜到這個事件是在瀏覽器取出歷史記錄並加載時觸發的。但實際上,它的條件是比較苛刻的,幾乎只有點擊瀏覽器的「前進」、「後退」這些導航按鈕,或者是由JavaScript調用的history.back()等導航方法,且切換先後的兩條歷史記錄都屬於同一個網頁文檔,纔會觸發本事件。

上面的「同一個網頁文檔」請理解爲JavaScript環境的document是同一個,而不是指基礎URL(去掉各種參數的)相同。也就是說,只要有從新加載發生(不管是跳轉到一個新站點仍是繼續在本站點),JavaScript全局環境發生了變化,popstate事件都不會觸發。

popstate事件是設計出來和前面的2個方法搭配使用的。通常只有在經過前面2個方法設置了同一站點的多條歷史記錄,並在其之間導航(前進或後退)時,纔會觸發這個事件。同時,前面2個方法所設置的狀態對象(第1個參數),也會在這個時候經過事件的event.state返還回來。

此外請注意,history.pushState()history.replaceState()自己調用時是不觸發popstate事件的。pop和push畢竟不同!

如何應用

HTML5 history API的內容很少,具體如何應用它來改進Ajax翻頁呢?

首先,在服務器端添加對URL狀態參數的支持,例如?page=3將會輸出對應頁碼的內容(後端模板)。也能夠是服務器端把對應頁碼的數據給JavaScript,由JavaScript向頁面寫入內容(前端模板)。

接下來,使用history.pushState(),在任一次翻頁的同時,也設置正確的帶參數的URL。代碼多是這樣:

newURL = "?page=" + pageNow; history.pushState(null, "", newURL); 

到此,就解決了F5刷新狀態還原的事了。

不過,尚未結束,在瀏覽器中點擊後退,例如從?page=3退到?page=2,會發現沒有變化。按道理說,這時候也應該對應變化。這就要用到popstate事件了。

window添加popstate事件,加入這種導航變化時的處理。代碼多是這樣(jQuery):

$(window).on("popstate", function(event) { // 取得以前經過pushState保存的state object,儘管本示例並不打算使用它。 // jQuery對event作了一層包裝,須要經過originalEvent取得原生event。 var state = event.originalEvent.state, // 本示例直接取URL參數來處理 reg = /page=(\d+)/, regMatch = reg.exec(location.search), // 第1頁的時候既能夠是 ?page=1,也能夠根本沒有page參數 pageNow = regMatch === null ? 1 : +regMatch[1]; updateByPage(pageNow); }); 

這樣,就完成了。這樣看起來是否會以爲還挺容易的呢?在支持HTML5 history API的瀏覽器中,以上部分就已經作到了帶頁碼記錄的Ajax翻頁。

有待斟酌的兼容性問題

根據caniuse上的數據,IE10+及其餘主流瀏覽器都支持HTML5 history API。爲保證不支持的瀏覽器不報錯,能夠加入是否支持HTML5 history API的判斷:

// 參考自 http://modernizr.com/download/#-history 源碼 var isHistoryApi = !!(window.history && history.pushState); // ... if(isHistoryApi){ // ... } 

這樣,一個Ajax翻頁,在支持HTML5 history API的瀏覽器上,將會智能地保存當前頁碼信息,而不支持的瀏覽器仍然能夠正常使用,只是不保存頁碼信息(就像改進前那樣)。我認爲,按照「漸進加強」的思路,這樣就是最好的了,也就是:只使用較少的代碼優化高級瀏覽器的使用體驗。

若是真的想要在各種瀏覽器裏都表現一致,擁有這樣的記錄功能呢?

這時候推薦使用Benjamin Lupton的History.js,它提供和HTML5 history API近似的api,會在不支持的瀏覽器裏回退到hash形式去處理歷史記錄。儘管爲了兼容這種hash的回退形式你可能要額外作點事(hash不會發送到服務器端),但它確實可讓你作到更廣範圍的兼容。

HTML5 history API並不完美

即便只考慮支持HTML5 history API的瀏覽器,它們對HTML5 history API的一些細節處理也會有差別和問題。History.js提供的只針對HTML5瀏覽器的版本,仍然包含了很多處理兼容問題的代碼。

可是,不完美也沒有關係。以個人測試結果,本文所介紹的簡單的寫法,就能夠在絕大部分支持HTML5 history API的瀏覽器上正常運行。若是你擔憂有哪些瀏覽器會有潛在問題,去測試那個瀏覽器就能夠了。你最後用於兼容處理的自寫代碼極可能遠比一個JavaScript庫少得多,畢竟,你也不必定會喜歡額外引入一個JavaScript庫來完成一個功能吧。

一些相關內容

地址欄裏的hash曾是過去被普遍用來記錄頁面狀態的標記,你能夠閱讀W3C Blog的這篇文章瞭解它的經歷。

如今能夠在不刷新的情況下操做瀏覽器地址欄和歷史記錄了,那同一站點的普通連接跳轉是否均可以轉變爲Ajax來提高使用體驗?是的,並且已經有了pjaxturbolinks這些專門完成這個功能的做品。

不僅是翻頁,HTML5 history API將尤爲適合用在大量使用Ajax、包含多個視圖的單頁應用。

爲一個頁面的每個狀態都生成一條歷史記錄不必定合適(會讓用戶的歷史記錄變多變亂),酌情使用replaceState()而不是pushState()來控制歷史記錄的數量。

結語

HTML5 history API簡單易學,很少的幾行代碼就能夠作到「狀態記錄」這個小小的改進,若是能夠由你選擇「漸進加強」,它還真的能夠上線!

--------------------------------------------------

 html4 history api===================+ history.length 歷史記錄條數+ history.go(n) n可爲正負數 任意前進或後退n步+ history.back(); 後退+ history.forward(); 前進html5 history api===================+ history.pushState(data, title, [url]); *往歷史記錄堆棧頂部添加一條記錄*     `data`: onpopstate事件的回調中做爲參數傳入    `title`: 頁面標題,目前瀏覽器都會忽略這個參數    `url`: 頁面地址 默認爲當前url+ history.replaceState(data, title, [url]); *替換當前歷史記錄,參數同pushState方法*+ history.state *存儲上述方法的 data數據,不一樣瀏覽器讀寫權限不同*+ window.onpopstate *響應pushState或replaceState的調用* 

相關文章
相關標籤/搜索