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;
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()有三个参数:一个状态对象、一个标题(如今会被忽略),一个可选的URL地址。下面来单独考察这三个参数的细节:
状态对象(state object) — 一个JavaScript对象,与用pushState()方法建立的新历史记录条目关联。不管什么时候用户导航到新建立的状态,popstate事件都会被触发,而且事件对象的state属性都包含历史记录条目的状态对象的拷贝。
任何可序列化的对象均可以被当作状态对象。由于FireFox浏览器会把状态对象保存到用户的硬盘,这样它们就能在用户重启浏览器以后被还原,咱们强行限制状态对象的大小为640k。若是你向pushState()方法传递了一个超过该限额的状态对象,该方法会抛出异常。若是你须要存储很大的数据,建议使用sessionStorage或localStorage。
标题(title) — FireFox浏览器目前会忽略该参数,虽然之后可能会用上。考虑到将来可能会对该方法进行修改,传一个空字符串会比较安全。或者,你也能够传入一个简短的标题,标明将要进入的状态。
地址(URL) — 新的历史记录条目的地址。浏览器不会在调用pushState()方法后加载该地址,但以后,可能会试图加载,例如用户重启浏览器。新的URL不必定是绝对路径;若是是相对路径,它将以当前URL为基准;传入的URL与当前URL应该是同源的,不然,pushState()会抛出异常。该参数是可选的;不指定的话则为文档当前URL。
某种意义上,调用pushState()有点相似于设置window.location='#foo',它们都会在当前文档内建立和激活新的历史记录条目。但pushState()有本身的优点:
document
中。注意pushState()方法永远不会触发hashchange事件,即使新的地址只变动了hash。
history.replaceState()操做相似于history.pushState(),不一样之处在于replaceState()方法会修改当前历史记录条目而并不是建立新的条目。
当你为了响应用户的某些操做,而要更新当前历史记录条目的状态对象或URL时,使用replaceState()方法会特别合适。
每当激活的历史记录发生变化时,都会触发popstate事件。若是被激活的历史记录条目是由pushState所建立,或是被replaceState方法影响到的,popstate事件的状态属性将包含历史记录的状态对象的一个拷贝。
在页面加载时,可能会包含一个非空的状态对象。这种状况是会发生的,例如,若是页面中使用pushState()或replaceState()方法设置了一个状态对象,而后用户重启了浏览器。当页面从新加载时,页面会触发onload事件,但不会触发popstate事件。可是,若是你读取 history.state 属性,你会获得一个与 popstate 事件触发时获得的同样的状态对象。
你能够直接读取当前历史记录条目的状态,而不须要等待popstate事件:
var currentState = history.state;
你可能须要History.js来解决跨浏览器兼容性问题。
曾说SEO和ajax是天敌。此前从Twitter开始流行Ajax+hash的方式调用内容,Google给出的解决方案是“#!~string”自动转换为“?_excaped_fragment_=~string”来抓取动态内容。但这无疑会很是麻烦:首先你须要对网站进行“?_excaped_fragment_=~string”的处理配置,并且,若是用户把网址“http://example.com/#!/~string”直接复制并分享的话,意味着网页还必须监听hashchange。不过若是你以为这个#!很好看就不要紧了。
然而HTML5的新接口pushState / replaceState就能够比较完美的解决问题,它避免了改变hash的问题,避免了用户不理解URL的形式感到疑惑,同时还有onpopstate提供监听,良好响应后退前进。并且它不须要这个URL真实存在。
HTML5提供history接口,把URL以state的形式添加或者替换到浏览器中,其实现函数正是 pushState 和 replaceState。
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是不能改变网页标题的哦。
window.history.replaceState( state, ~title, ~href);
pushState()能够建立历史,能够配合popstate事件,而replaceState()则是替换掉当前的URL,不会产生历史。
只能用同域的URL替换,例如你不能用http://baidu.com去替换http://google.com。并且state对象不存储不可序列化的对象如DOM。
如今用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){ ... });
想要良好的支持浏览器的历史前进后退操做,应当部署popstate监听:
window.addEventListener('popstate', function(evt){
var state = evt.state;
var newTitle = ajax_Load(state.url); //你自定义的ajax加载函数,例如它会返回newTitle
document.title=newTitle;
}, false);
提醒,你能够经过setRequestHeader()来让服务器端配合你的ajax请求输出专门的内容。
已经在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})
});}