pushState onpopstate

參考MDN:

https://developer.mozilla.org/zh-CN/docs/DOM/Manipulating_the_browser_historyhtml

https://developer.mozilla.org/zh-CN/docs/Mozilla_event_reference/popstatehtml5

 

 window 對象經過history對象提供對覽器歷史記錄的訪問能力。它暴露了一些很是有用的方法和屬性,讓你在歷史記錄中自由前進和後退,而在HTML5中,更能夠操縱歷史記錄中的數據。jquery

歷史記錄概覽

能夠經過back(),forward()和go()方法在用戶的歷史記錄中前進與後退。git

前進與後退

在歷史記錄中後退,能夠這麼作:github

window.history.back();

這就像用戶點擊瀏覽器的後退按鈕同樣。ajax

相似的,你能夠前進,就像在瀏覽器中點擊前進按鈕,像這樣:算法

window.history.forward();

移動到指定的歷史記錄點

經過指定一個相對於當前頁面位置的數值,你可使用go()方法從當前會話的歷史記錄中加載頁面(當前頁面位置索引值爲0,上一頁就是-1,下一頁爲1)。瀏覽器

要後退一頁(至關於調用back()):安全

window.history.go(-1);
 

向前移動一頁(至關於調用forward()):服務器

window.history.go(1);
 

相似的,傳遞參數「2」,你就能夠向前移動2頁。

你能夠查看length屬性值,瞭解歷史記錄棧中一共有多少頁:

var numberOfEntries = window.history.length;
注意: IE中能夠給go()方法傳遞URL字符串參數,這是非標準的方法,而且Gecko不支持。

添加和修改歷史記錄條目

 

HTML5引進了history.pushState()方法和history.replaceState()方法,它們容許你逐條地添加和修改歷史記錄條目。這些方法能夠協同window.onpopstate事件一塊兒工做。

使用 history.pushState() 會改變 referrer 的值,而在你調用方法後建立的 XMLHttpRequest 對象會在 HTTP 請求頭中使用這個值。referrer的值則是建立 XMLHttpRequest 對象時所處的窗口的URL。

案例

假設 http://mozilla.org/foo.html 將執行以下JavaScript代碼:

var stateObj = { foo: "bar" };
history.pushState(stateObj, "page 2", "bar.html");

這將讓瀏覽器的地址欄顯示http://mozilla.org/bar.html,但不會加載bar.html頁面也不會檢查bar.html是否存在。

假設如今用戶導航到了http://google.com,而後點擊了後退按鈕,此時,地址欄將會顯示http://mozilla.org/bar.html,而且頁面會觸發popstate事件,該事件中的狀態對象(state object)包含stateObj的一個拷貝。該頁面看起來像foo.html,儘管頁面內容可能在popstate事件中被修改。

若是咱們再次點擊後退按鈕,URL將變回http://mozilla.org/foo.html,文檔將觸發另外一個popstate事件,此次的狀態對象爲null。回退一樣不會改變文檔內容。

pushState()方法

pushState()有三個參數:一個狀態對象、一個標題(如今會被忽略),一個可選的URL地址。下面來單獨考察這三個參數的細節:

  • 狀態對象(state object) — 一個JavaScript對象,與用pushState()方法建立的新歷史記錄條目關聯。不管什麼時候用戶導航到新建立的狀態,popstate事件都會被觸發,而且事件對象的state屬性都包含歷史記錄條目的狀態對象的拷貝。

    任何可序列化的對象均可以被當作狀態對象。由於FireFox瀏覽器會把狀態對象保存到用戶的硬盤,這樣它們就能在用戶重啓瀏覽器以後被還原,咱們強行限制狀態對象的大小爲640k。若是你向pushState()方法傳遞了一個超過該限額的狀態對象,該方法會拋出異常。若是你須要存儲很大的數據,建議使用sessionStorage或localStorage。

  • 標題(title) — FireFox瀏覽器目前會忽略該參數,雖然之後可能會用上。考慮到將來可能會對該方法進行修改,傳一個空字符串會比較安全。或者,你也能夠傳入一個簡短的標題,標明將要進入的狀態。

  • 地址(URL) — 新的歷史記錄條目的地址。瀏覽器不會在調用pushState()方法後加載該地址,但以後,可能會試圖加載,例如用戶重啓瀏覽器。新的URL不必定是絕對路徑;若是是相對路徑,它將以當前URL爲基準;傳入的URL與當前URL應該是同源的,不然,pushState()會拋出異常。該參數是可選的;不指定的話則爲文檔當前URL。

注意: 在 Gecko 2.0 (Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1) 至 Gecko 5.0 (Firefox 5.0 / Thunderbird 5.0 / SeaMonkey 2.2) 中,傳入的對象使用JSON來進行序列化。從 Gecko 6.0 (Firefox 6.0 / Thunderbird 6.0 / SeaMonkey 2.3)開始,對象使用 結構化拷貝算法來進行序列化。這將容許更多類型的對象可以安全傳入。

某種意義上,調用pushState()有點相似於設置window.location='#foo',它們都會在當前文檔內建立和激活新的歷史記錄條目。但pushState()有本身的優點:

  • 新的URL能夠是任意的同源URL,與此相反,使用window.location方法時,只有僅修改 hash 才能保證停留在相同的document中。
  • 根據我的須要來決定是否修改URL。相反,設置window.location='#foo',只有在當前hash值不是foo時才建立一條新歷史記錄。
  • 你能夠在新的歷史記錄條目中添加抽象數據。若是使用基於hash的方法,你只能把相關數據轉碼成一個很短的字符串。

注意pushState()方法永遠不會觸發hashchange事件,即使新的地址只變動了hash。

replaceState()方法

history.replaceState()操做相似於history.pushState(),不一樣之處在於replaceState()方法會修改當前歷史記錄條目而並不是建立新的條目。

當你爲了響應用戶的某些操做,而要更新當前歷史記錄條目的狀態對象或URL時,使用replaceState()方法會特別合適。

注意: 在 Gecko 2.0 (Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1) 至 Gecko 5.0 (Firefox 5.0 / Thunderbird 5.0 / SeaMonkey 2.2) 中,傳入的對象使用JSON來進行序列化。從 Gecko 6.0 (Firefox 6.0 / Thunderbird 6.0 / SeaMonkey 2.3)開始,對象使用 結構化拷貝算法來進行序列化。這將容許更多類型的對象可以安全傳入。

popstate事件

每當激活的歷史記錄發生變化時,都會觸發popstate事件。若是被激活的歷史記錄條目是由pushState所建立,或是被replaceState方法影響到的,popstate事件的狀態屬性將包含歷史記錄的狀態對象的一個拷貝。

案例見 window.onpopstate

讀取當前狀態

在頁面加載時,可能會包含一個非空的狀態對象。這種狀況是會發生的,例如,若是頁面中使用pushState()或replaceState()方法設置了一個狀態對象,而後用戶重啓了瀏覽器。當頁面從新加載時,頁面會觸發onload事件,但不會觸發popstate事件。可是,若是你讀取 history.state 屬性,你會獲得一個與  popstate 事件觸發時獲得的同樣的狀態對象。

你能夠直接讀取當前歷史記錄條目的狀態,而不須要等待popstate事件:

var currentState = history.state;

瀏覽器兼容性

 

Feature Chrome Firefox (Gecko) Internet Explorer Opera Safari
replaceState, pushState 5 4.0 (2.0) 10 11.50 5.0
history.state 18 4.0 (2.0) 10 11.50 6.0

 

你可能須要History.js來解決跨瀏覽器兼容性問題

其餘資料

若是你玩過Google+,看到過YouTube的新界面,便會體驗到這個HTML5的新功能。使用pushState + Ajax(pjax),能夠實現網頁的ajax加載,同時又能完成URL的改變而沒有網頁跳轉刷新的跡象,就像是改變了網頁的hash(#)同樣。

舊的解決方案

曾說SEO和ajax是天敵。此前從Twitter開始流行Ajax+hash的方式調用內容,Google給出的解決方案是「#!~string」自動轉換爲「?_excaped_fragment_=~string」來抓取動態內容。但這無疑會很是麻煩:首先你須要對網站進行「?_excaped_fragment_=~string」的處理配置,並且,若是用戶把網址「http://example.com/#!/~string」直接複製並分享的話,意味着網頁還必須監聽hashchange。不過若是你以爲這個#!很好看就不要緊了。

twtter hash

新的解決方案: pushState

然而HTML5的新接口pushState / replaceState就能夠比較完美的解決問題,它避免了改變hash的問題,避免了用戶不理解URL的形式感到疑惑,同時還有onpopstate提供監聽,良好響應後退前進。並且它不須要這個URL真實存在。

HTML5 的 pushState+Ajax

HTML5提供history接口,把URL以state的形式添加或者替換到瀏覽器中,其實現函數正是 pushState 和 replaceState。

pushState 例子

pushState() 的基本參數是:

window.history.pushState(state, title, url);

其中state和title均可覺得空,可是推薦不爲空,應當建立state來配合popstate監聽。

例如,咱們經過pushState現改變URL而不刷新頁面。

var state = ( {

url: ~href, title: ~title, ~additionalKEY: ~additionalVALUE

} );

window.history.pushState(state, ~title, ~href);

其中帶有「~」符號的是自定義內容。就能夠把這個~href(URL)推送到瀏覽器的歷史裏。若是想要改變網頁的標題,應該:

document.title= ~newTitle;

注意只是pushState是不能改變網頁標題的哦。

 

replaceState 同理

window.history.replaceState( state, ~title, ~href);

pushState、replaceState 的區別

pushState()能夠建立歷史,能夠配合popstate事件,而replaceState()則是替換掉當前的URL,不會產生歷史。

限制因素

只能用同域的URL替換,例如你不能用http://baidu.com去替換http://google.com。並且state對象不存儲不可序列化的對象如DOM。

Ajax 配合 pushState 例子

如今用Ajax + pushState來提供全新的ajax調用風格。以jQuery爲例,爲了SEO須要,應該爲a標籤的onclick添加方法。

$("~target a").click(function(evt){

evt.preventDefault(); // 阻止默認的跳轉操做

var uri=$(this).attr('href');

var newTitle=ajax_Load(uri); // 你自定義的Ajax加載函數,例如它會返回newTitle

document.title=newTitle; // 分配新的頁面標題

if(history.pushState){

var state=({

url: uri, title: newTitle

});

window.history.pushState(state, newTitle, uri);

}else{ window.location.href="#!"+~fakeURI; } // 若是不支持,使用舊的解決方案

return false;

});

function ajax_Load(uri){ ... return newTitle; } // 你自定義的ajax函數,例如它會返回newTitle

便可完成pushState。至於新標題newTitle的獲取就是另外的問題了,例如你能夠爲a標籤分配data-newtitle=~title屬性並屆時讀取,或者若是你用的$.ajax()函數,能夠用$(result).filter("title").text()來獲取。

另外若是須要對新加載的頁面的鏈接一樣使用這個ajax,則須要對新內容的a標籤從新部署,例如

$("~newContentTarget a").click(function(evt){ ... });

pushState 配合 popstate 監聽

想要良好的支持瀏覽器的歷史前進後退操做,應當部署popstate監聽:

window.addEventListener('popstate', function(evt){

var state = evt.state;

var newTitle = ajax_Load(state.url); //你自定義的ajax加載函數,例如它會返回newTitle

document.title=newTitle;

}, false);

提醒,你能夠經過setRequestHeader()來讓服務器端配合你的ajax請求輸出專門的內容。

 

jQuery + PJAX 插件

已經在github上發佈,有人把PJAX作成了jQuery插件,方便調用,節省大量代碼:

if ($.support.pjax) {

$(document).on('click', 'a[data-pjax]', function(event) {

var container = $(this).closest('[data-pjax-container]')

$.pjax.click(event, {container: container})

});}

相關文章
相關標籤/搜索