【東網(wǎng)技術(shù)大咖】HTML5 history api搭配ajax實(shí)現(xiàn)前進(jìn)后退
發(fā)布時(shí)間: 2017-04-01 15:16:39
AJAX即“Asynchronous Javascript And XML”(異步JavaScript和XML),是指一種創(chuàng)建交互式網(wǎng)頁(yè)應(yīng)用的網(wǎng)頁(yè)開(kāi)發(fā)技術(shù)。通過(guò)在后臺(tái)與服務(wù)器進(jìn)行少量數(shù)據(jù)交換,AJAX 可以使網(wǎng)頁(yè)實(shí)現(xiàn)異步更新。這意味著可以在不重新加載整個(gè)網(wǎng)頁(yè)的情況下,對(duì)網(wǎng)頁(yè)的某部分進(jìn)行更新。
在良品鋪?zhàn)由芷谙到y(tǒng)開(kāi)發(fā)中,遇到過(guò)這樣一種情況,我們使用ajax來(lái)加載不同標(biāo)簽頁(yè)的數(shù)據(jù),用戶在使用的時(shí)候想用前進(jìn)后退來(lái)切換標(biāo)簽頁(yè),發(fā)現(xiàn)直接退出了當(dāng)前功能,于是向我們反饋了這一問(wèn)題。確實(shí),在用戶使用中,不同標(biāo)簽頁(yè)的切換對(duì)于他們來(lái)說(shuō)是意味著不同的頁(yè)面,自然而然會(huì)想前進(jìn)后退來(lái)切換,但是實(shí)際中ajax是無(wú)法前進(jìn)后退的,便造成很糟糕的體驗(yàn),比如填好的表單后退后全部消失,查詢好的數(shù)據(jù)后退一下又需要重新查詢。
那么如何解決這一問(wèn)題呢?在實(shí)際開(kāi)發(fā)中,單純使用ajax,瀏覽器地址是不會(huì)發(fā)生變化,前進(jìn)后退是無(wú)法控制標(biāo)簽切換的,這個(gè)時(shí)候我們必須借助HTML5里新的history api搭配ajax來(lái)解決這個(gè)問(wèn)題。
我們先來(lái)回顧一下現(xiàn)有html規(guī)范中,history對(duì)象的三個(gè)方法:
方法 |
描述 |
back() |
加載 history 列表中的前一個(gè) URL。 |
forward() |
加載 history 列表中的下一個(gè) URL。 |
go() |
加載 history 列表中的某個(gè)具體頁(yè)面。 |
使用ajax時(shí),由于地址未發(fā)生變化,這三個(gè)api是無(wú)法進(jìn)行頁(yè)面控制的。
新的HTML5標(biāo)準(zhǔn)為我們解決了這一難題,html5標(biāo)準(zhǔn)中history對(duì)象增加了兩個(gè)方法。
方法 |
描述 |
pushState() |
存儲(chǔ)當(dāng)前歷史記錄點(diǎn) |
replaceState() |
替換當(dāng)前歷史記錄點(diǎn) |
還有一個(gè)搭配使用的popstate事件,用于監(jiān)聽(tīng)url的變化。
pushState能改變當(dāng)前url(添加新的歷史記錄點(diǎn)),popstate監(jiān)聽(tīng)url的變化,瀏覽器的前進(jìn)后退改變url(在歷史記錄點(diǎn)之間切換),三者結(jié)合便能解決ajax中前進(jìn)后退的問(wèn)題。
讓我們先來(lái)看下pushState,replaceState,popstate這個(gè)三個(gè)新的api的詳細(xì)介紹。
存儲(chǔ)的方式類似于數(shù)組的入棧(Array.push()),在window.history里新增一個(gè)歷史記錄點(diǎn),例如:
// 當(dāng)前的url為:http://www.example.com/demo.html
var json={time:new Date().getTime()};
//@狀態(tài)對(duì)象:記錄歷史記錄點(diǎn)的額外對(duì)象,可以為空
//@頁(yè)面標(biāo)題:目前所有瀏覽器都不支持
//@可選的url:瀏覽器不會(huì)檢查url是否存在,只改變url,url必須同域,不能跨域
window.history.pushState(json,"","http://www.example.com/demo.html#test");
var state = {title: title,
url: options.url,
otherkey: othervalue
};
window.history.pushState(state, document.title, url);
執(zhí)行了pushState方法后,頁(yè)面的url地址為:
http://www.example.com/demo.html#test
window.history.replaceState和window.history.pushState類似,不同之處在于replaceState不會(huì)在window.history里新增歷史記錄點(diǎn),其效果類似于window.location.replace(url),都是不會(huì)在歷史記錄點(diǎn)里新增一個(gè)記錄點(diǎn)的。當(dāng)你為了響應(yīng)用戶的某些操作,而要更新當(dāng)前歷史記錄條目的狀態(tài)對(duì)象或URL時(shí),使用replaceState()方法會(huì)特別合適。
監(jiān)聽(tīng)歷史記錄點(diǎn),直觀的可認(rèn)為是監(jiān)聽(tīng)URL的變化,但會(huì)忽略URL的hash部分,可以通過(guò)window.onpopstate來(lái)監(jiān)聽(tīng)url的變化,并且可以獲取存儲(chǔ)在該歷史記錄點(diǎn)的狀態(tài)對(duì)象,也就是上文說(shuō)到的json對(duì)象。
值得注意的是:window.history.pushState和window.history.replaceState不會(huì)觸發(fā)onpopstate事件。
了解了這三個(gè)新的api,再讓我們看看如何實(shí)際開(kāi)發(fā)中來(lái)進(jìn)行應(yīng)用。
一個(gè)分頁(yè)的例子,點(diǎn)擊不同頁(yè)面使用ajax加載不同的地市編號(hào)和地市名稱,默認(rèn)加載第一頁(yè),如果帶有頁(yè)碼n會(huì)加載第n頁(yè)的數(shù)據(jù),前進(jìn)后退能夠在不同頁(yè)面進(jìn)行切換
頁(yè)面部分:
Js代碼:
var page_data=[{
????"CityID": 159,
????"name": "武漢市",
????"ProID": 17,
????"CitySort": 159
}, {
????"CityID": 160,
????"name": "襄樊市",
????"ProID": 17,
????"CitySort": 160
}, {
????"CityID": 161,
????"name": "鄂州市",
????"ProID": 17,
????"CitySort": 161
}, {
????"CityID": 162,
????"name": "孝感市",
????"ProID": 17,
????"CitySort": 162
}, {
????"CityID": 163,
????"name": "黃岡市",
????"ProID": 17,
????"CitySort": 163
}];
//保存當(dāng)前頁(yè)碼
var page=-1;
function loadPage(no){
//加載數(shù)據(jù)
loadData(no);
//添加記錄點(diǎn),每次push都會(huì)添加一個(gè)記錄點(diǎn),即使url相同
//防止重復(fù)添加
if(page==no){
return;
}else{
page=no;
push(no);
}
}
?
function loadData(no){
//加載分頁(yè)數(shù)據(jù),實(shí)際應(yīng)用中為ajax取分頁(yè)數(shù)據(jù)
document.getElementById("content").innerHTML = page_data[no-1].CityID + " --- " + page_data[no-1].name;
}
?
function push(no){
//json對(duì)象
var state = {
no: no
};
var title = "第"+no+"頁(yè)";
var query = "#"+no;
//添加記錄點(diǎn)
history.pushState(state,title,location.href.split("?")[0].split("#")[0] + query );
}
?
//監(jiān)聽(tīng)url變化,根據(jù)url和json對(duì)象進(jìn)行不同的處理,相當(dāng)于路由的功能
window.onpopstate = function(event){
var state = event.state;
loadData(state.no);
};
?
//初始化,默認(rèn)加載第1頁(yè),如果帶有頁(yè)面n,則加載第n頁(yè)
function init(){
page = location.href.split("?")[0].split("#")[1];
if(undefined==page){
loadPage(1);
}else{
loadPage(page);
}
}
init();
效果截圖:
可以看到,瀏覽器里面的鏈接變?yōu)閡rl+#+頁(yè)碼,這個(gè)時(shí)候可以通過(guò)前進(jìn)后退來(lái)觸發(fā)onpopstate 來(lái)實(shí)現(xiàn)ajax加載不同頁(yè)面的數(shù)據(jù),監(jiān)聽(tīng)事件觸發(fā)后可以根據(jù)json對(duì)象和鏈接里的參數(shù)來(lái)進(jìn)行不同的處理,以達(dá)到切換標(biāo)簽的效果,用戶體驗(yàn)時(shí)的感覺(jué)和前進(jìn)后退是沒(méi)有任何差異的,由于沒(méi)有刷新頁(yè)面,體驗(yàn)甚至?xí)谩?/span>
至此,問(wèn)題得到圓滿解決。