histroy.pushState(stateObj, title, URL);
histroy.replaceState(stateObj, title, URL);
history.pushState({a: 'aaaa'}, 'page2', 'xxx.html')
复制代码
history和ajax的区别:
我们设想下,当在浏览器窗口打开第一个地址,比如url1时,这时history中就有了url1这个记录,且length属性值为1,history对象中有个当前页面指针(从概念上可以这么理解)指向url1。
如果再打开一个url2页面(无论是通过在地址栏直接输入、或通过url1中的链接或js代码打开),这时history中就有了url1和url2这两个记录,是一个有序的列表,这时length属性值为2,history对象中的当前页面指针指向url2,这时url2是最新的页面,页面不可以前进,但可以后退到url1。
这时如果点击浏览器本身提供的后退按钮(或用js调用back方法),这时url1页面会被重新加载显示,history对象的length仍然为2,url1和url2组成的列表仍然不变,但history对象中的当前页面指针指向url1了,这时就不能后退但可以前进了。可以理解成一个数据结构中的双向链表机制。
通过上面的描述我们可以看出,我们说的历史记录都是指一个完整的页面请求url,而ajax并不是一个完整的页面请求,因此浏览器无法记录ajax的操作信息。
具体案例:
我们来设想这样一个应用。一个页面来显示一篇长文章,该文章内容很长,分为很多章节。我们希望页面不会一次把所有章节的内容都加载起来,而是有一个章节导航,点击每个章节链接,通过jax加载具体章节的内容,而其它页面部分不需要要变化。
我们先看下传统的实现代码(注意,这里只注意核心逻辑代码的实现,其它的页面布局等尽量简化):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test2</title>
<style type="text/css">
div {
padding-bottom:100px;
}
</style>
<script type="text/javascript" src="jquery.min.js"></script>
</head>
<body>
<div style="float:left;border:1px solid red;margin:20px">
<p><a href="javascript:;" id="section1">第1章</a></p>
<p><a href="javascript:;" id="section2">第2章</a></p>
<p><a href="javascript:;" id="section3">第3章</a></p>
</div>
<div style="float:left;border:1px solid red;margin:20px" id="content">
</div>
<script>
$(function(){
//添加链接的处理事件
$("a").click(ajax);
//加载默认的章节,默认显示第1章
$("#section1").trigger("click");
});
function ajax(event){
//实际的流程是发起ajax请求,获取内容并显示。这里为了简化,没有写实际的ajax请求。
//这段代码应该在ajax的请求响应中编写。
$("#content").html(this.id+"的内容");
var title = this.id;
document.title = title;
}
</script>
</body>
</html>
复制代码
在浏览器加载该页面,当我们点击不同的章节链接时,内容会跟着变化,浏览器的标题也跟着变化。但是:
1)回退、前进按钮用不了
2)当我们刷新页面时,不管当前在哪个章节,都会重新回到第一个章节。
3)地址栏的url没有变化,也意味着我们没法把某个章节的地址保存下来,以后再次打开直接显示该章节内容。
上面就是传统ajax应用的一些弊端。下面我们就来解决这些问题。
我们先给出解决代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test2</title>
<style type="text/css">
div {
padding-bottom:100px;
}
</style>
<script type="text/javascript" src="jquery.min.js"></script>
</head>
<body>
<div style="float:left;border:1px solid red;margin:20px">
<p><a href="javascript:;" id="section1">第1章</a></p>
<p><a href="javascript:;" id="section2">第2章</a></p>
<p><a href="javascript:;" id="section3">第3章</a></p>
</div>
<div style="float:left;border:1px solid red;margin:20px" id="content">
</div>
<script>
$(function(){
//添加链接的处理事件
$("a").click(ajax);
//加载默认的章节
changeContent();
//添加popstate事件
$(window).on("popstate",function(){
changeContent();
});
});
function changeContent(){
var query = location.href.split("?")[1];
if (!query) {
// 如果没有查询条件,则显示默认第1个章节
history.replaceState(null, "",
location.href + "?name=" + $("#section1")[0].id);
changeContent();
} else {
//触发按钮click事件,加载内容,
//注意不要漏了true参数,这样可以和用户直接点击触发的页面变化区别开来
$("#"+query.split("=")[1]).trigger("click",true);
}
}
function ajax(event,isPopstate){
$("#content").html(this.id+"的内容");
var title = this.id;
document.title = title;
if(!isPopstate){
history.pushState(null, "", location.href.split("?")[0] + "?name=" + title);
}
}
</script>
</body>
</html>
复制代码
我们先看changeContent方法,该方法首先获取页面的url地址,判断该地址是否有查询条件(是否带章节信息),如果没有,认为要显示第一章节。我们利用history的replaceState方法来改变当前的url,加上name=section1的查询条件,表示是第1章。因为replaceState方法不会改变页面内容,因此还需要接着再调用changeContent方法。如果地址带了查询条件,认为已经指定显示某个章节内容,这时触发章节链接的click事件。
我们再看ajax方法,就是章节链接的click事件响应函数,为了简化,该函数没有发起实际的ajax请求,而是相当于直接处理ajax返回的结果。首先是用得到的结果更新页面(这里是直接写死的),然后更新标题,这与传统的ajax做法一样。关键的区别是,判断该方法如果是用户点击的(不是onpopstate事件处理的),就会调用history对象的pushState方法来将当前页面信息保存到history对象中,并新增一个记录信息代表ajax请求后的页面。
changeContent方法同样是onpopstate事件的处理函数,其功能就是利用获取到的url信息(保存在history记录)中,来通过ajax获取到对应的内容,让页面显示相应的信息。
从用户感知上看,就跟正常的回退、前进导致的页面切换一样。用户感觉不到是ajax请求,还以为就是多个独立的页面在切换。
总结:
调用history.pushState()或者history.replaceState()不会触发popstate事件。popstate事件只会在浏览器某些行为下触发,比如点击后退、前进按钮(或者在JavaScript中调用history.back()、history.forward()、history.go()方法)。
pushState()方法是在历史记录中增加一条新的记录。
replaceState()方法是将当前的历史记录给替换掉。