首先,写一个请求:
//当前第几页var pageIndex = 0;//请求函数function makeRequest(pageIndex){var request = new XMLHttpRequest();request.onreadystatechange = stateChange;//请求传两个参数,一个是当前第几页,另一个是每页的数据条数request.open("GET", "/getBook?page=" + pageIndex + "&limit=4", true);request.send(null);function stateChange(){//状态码为4,表示loaded,请求完成if(request.readyState !== 4 ){return;}//请求成功if(request.status >= 200 && request.status < 300 || request.status === 304){var books = JSON.parse(request.responseText);renderPage(books); }}}拿到数据后进行渲染:
function renderPage(books){var bookHtml = "<table>" +"<tr>" +"<th>书名</th>" +"<th>作者</th>" +"<th>版本</th>" +"</tr>";for(var i in books){bookHtml += "<tr>" +"<td>" + books[i].book_name + "</td>" +"<td>" + books[i].author + "</td>" +"<td>" + books[i].edition + "</td>" +"</tr>";}bookHtml += "</table>";bookHtml += "<button>上一页</button>" + "<button onclick="nextPage();">下一页</button>";var section = document.createElement("section");section.innerHtml = bookHtml;document.getElementById("book").appendChild(section); }这样一个基本的ajax请求就搭起来了,然后再响应“下一页”按钮:
function nextPage(){//将页面的index加1pageIndex++;//重新发请求和页面加载makeRequest(pageIndex);}
function nextPage(){pageIndex++;makeRequest(pageIndex);//存放当前页面的数据window.history.pushState({page: pageIndex}, null, window.location.href); }
//如果用户点击返回或者前进按钮window.addEventListener("popstate", function(event){var page = 0;//由于第一页没有pushState,所以返回到第一页的时候是没有数据的,因此得做下判断if(event.state !== null){page = event.state.page;}makeRequest(page); pageIndex = page;});state数据通过event传进来,这样就可以得到pageIndex。
但是,这样实现还有问题,在第二页的时候如果刷新页面的话,会发生错乱,如下所示:首先点下一页到第二页,然后刷新页面,出现第一页,再点下一页,出现第二页,点返回时出现问题,显示还是第二页,不是期望的第一页,直到再次点返回时才是第一页:
从右边的工具栏可以发现,点第一次返回的时候获取到的pageIndex仍然是1。对于这种情况,需要分析history模型,如下所示:
可以理解为对history的操作,浏览器有一个队列,用来存放访问的记录,包括每个访问的网址还有state数据。一开始,队列的首指针指向page = 0的位置,点下一页时,执行了pushState,在这个队列插入了一个元素,同时通过pushState操作记录了这个元素的url和state数据。 在这里可以看出,pushState的操作最重要的作用还是给history队列插入元素,这样浏览器的后退按钮才不是置灰的状态,其次才是上面说的存放 数据。点后退的时候,队首指针后退一步指向page = 0的位置,点前进时又前进指向page = 1的位置。
如果在page = 1的位置刷新页面,模型是这个样子的:
在第2步刷新的时候,页面的pageIndex又恢复成默认值0,所以page = 0,显示第一页数据,但是history所用的队列并没有改变。然后再点下一页时,又给这个队列push了一个元素,这个队列就有两个pageIndex 为1的元素,所以必须得两次返回才能回到page = 0的位置,也就是上面说的错乱的情况。
根据上面的分析,这样的实现是有问题的,一但用户不是在page = 0的位置刷新页面,就会出现需要点多次返回按钮才能够回到原先的页面。
所以得在刷新的时候,把当前页的state数据更新一下,用replaceState,替换队列队首指针的数据,也就是当前页的数据。方法是页面初始化时replace一下:
window.history.replaceState({page: pageIndex /*此处为0*/}, null, window.location.href);
这样模型就变成:
但其实用户刷新的时候更希望的是还是显示当前页,而不是回到第一页。一个解决办法是用当前页的window.history.state数据,这个属性浏览器支持得比较晚。在页面初始化时设置pageIndex时就从history.state取:
var pageIndex = window.history.state === null ? 0 : window.history.state.page;safari里面的history.state是最近执行pushState传入的数据,因此这个办法在chrome/firefox里面行得通,但是safari行不通。
//页面初始化,取当前第几页先从localStorage取var pageIndex = window.localStorage.pageIndex || 0;function nextPage(){//将页面的index加1,同时存放在localStoragewindow.localStorage.pageIndex = ++pageIndex;//重新发请求和页面加载makeRequest(pageIndex);window.history.pushState({page: pageIndex}, null, window.location.href); }window.addEventListener("popstate", function(event){var page = 0;if(event.state !== null){page = event.state.page;}makeRequest(page); //点击返回或前进时,需要将page放到localStoragewindow.localStorage.pageIndex = page;});将页面中所有改变pageIndex的地方,同时放到localStorage。这样刷新页面的时候就可以取到当前页的pageIndex。
//当前第几页 var pageIndex = window.location.search.replace("?page=", "") || ; function nextPage(){ //将页面的index加 ++pageIndex; //重新发请求和页面加载 makeRequest(pageIndex); window.history.pushState(null, null, "?page=" + pageIndex); }注意,一旦执行了第8行的pushState,当前网址的地址就会发生变化。
可以看到history是一个数组,它的作用是让用户拿到history.length,当前的长度,但是填充的内容是不确定的。
除了使用history之外,还有借助hash的方法,网易新闻就是使用了这样的方法:
//当前第几页 var pageIndex = window.location.hash.replace("#page=", "") || ; function nextPage(){makeRequest(pageIndex); window.location.hash = "#page=" + pageIndex; } window.addEventListener("hashchange", function(){ var page = window.location.hash.replace("#page=", "") || ; makeRequest(page); });关于支持性,参考caniuse网站:history IE10及以上支持,hashchange的支持性较好,IE8及以上都支持。