HTML5 history API
HTML5 history API
标签(空格分隔): JavaScript
DOM
window
对象通过history
对象提供了对浏览器历史的访问。它暴露了很多有用的方法和属性,允许你在用户浏览历史中向前和向后跳转,同时,从HTML5开始提供了对history栈中内容的操作。
一、 在history中跳转
使用back()
,forward()
,go()
方法来完成在用户历史记录中向后和向前跳转。
// 在history中向后跳转
window.history.back();
window.history.go(-1);
// 向前跳转
window.history.forward();
window.history.go(1);
// 当前页
window.history.go(0);
二、添加和修改历史记录中的条目 (pushState()
和replaceState()
)
HTML5引入了history.pushState()
和history.replaceState()
方法,他们分别可以添加和修改历史记录条目。这些方法通常与window.onpopstate
配合使用。
使用history.pushState()
可以改变referrer(引用),它在用户发送XMLHttpRequest
请求时在HTTP头部使用,改变state后创建的XMLHttpRequset
对象的referrer都会被改变。因为referrer是标识创建XMLHttpRequest
对象时this
所代表的window对象中document的URL。
pushState()方法示例
假设有以下几个文件
假设在http://demo.com/foo.html中执行了一下js代码:
var stateObj = { foo: 'bar' };
window.history.pushState(stateObj, 'page 2', 'bar.html');
这将使浏览器地址栏显示为http://demo.com/bar.html,但并不会导致浏览器加载bar.html,甚至不会检查bar.html是否存在。如下图所示:
现在假设用户又访问了下面的链接http://demo.com/other.html,然后点击了返回按钮。此时,地址栏将显示http://demo.com/bar.html,同时页面会触发popstate事件,事件对象state中包含了stateObj
的一份拷贝。页面本身与foo.html
一样,尽管其在popstate
事件中可能会修改自身内容。
如果我们再次点击返回按钮,页面URL会变为http://demo.com/foo.html,文档对象document会触发另外一个popstate
事件,这一次的事件对象state object为null。这里也一样,返回并不改变文档的内容,尽管文档在接受popstate
事件时可能会改变自己的内容,其内容仍与之前的展现一致。
pushState()方法
pushState()
需要三个参数:一个状态对象,一个标题(目前被忽略)和一个URL(可选的)。
-
状态对象
状态对象state是一个js对象,通过pushState()创建新的历史记录条目。无论什么时候用户导航到新的状态,popstate事件就会被触发,且该事件的state属性包含该历史记录条目状态对象的副本。
状态对象可以是能被序列化的任何东西。原因在于火狐将状态对象保存在用户的磁盘上,以便用户重启浏览器时使用,我们规定了状态对象在序列化表示后有640k的大小限制。如果你给pushState()方法传了一个序列化后大于640k状态对象,该方法会抛出异常。如果你需要更大的空间,建议使用sessionStorage以及localStorage。
-
标题
火狐目前忽略这个参数,但未来可能会用到。传递一个空字符串在这里是安全的,而在将来这是不安全的。二选一的话,你可以为跳转的state传递一个短标题。
-
URL
该参数定义了新的历史URL记录。注意,调用pushState()后浏览器并不会立即加载这个URL,但可能会在稍后某些情况下加载这个URL,比如在用户重新打开浏览器时。新URL不必须为绝对路径。如果新URL是相对路径那么它将被作为相对于当前URL处理。新URL必须与当前URL同源,否则pushState()会抛出一个异常。该参数是可选的,缺省为当前URL。
某种意义上,调用pushState()
与设置widnow.location = '#ff'
类似,二者都会在当前页面创建并激活新的历史记录。但pushState()
有以下几条优点:
- 新的URL可以是与当前URL同源的任意URL。而设置
window.location
仅当你只修改了哈希值时才保持同一个document。 - 如果需要,你可以不必改变URL。而设置
window.location = '#foo';
在当前哈希不是#foo
的情况下,仅仅是新建了一个新的历史记录选项。 - 你可以为新的历史记录项关联任意数据。而基于哈希值的方式,则必须将所有相关数据编码到一个短字符串里。
- 假如
标题
在之后会被浏览器用到,那么这个数据是可以被使用的(哈希则不能)。
注意pushState()
绝对不会触发hashchange
事件,即是是新的URL与旧的URL仅哈希不同也不会触发。
在XUL文档中,它创建指定的XUL元素。
在其它文档中,它创建一个命名空间为URI为null的元素。
replaceState()方法
history.replaceState()
的使用与history.pushState()
非常相似,区别在于replaceState()
是修改了当前的历史记录项而不是新建一个。注意这并不会阻止其在全局浏览器历史记录中创建一个新的历史记录项。
replaceState()
的使用场景在于为了响应用户操作,你想要更新状态对象state或者当前历史记录的URL。
replaceState()示例
假设http://demo.com/foo.html执行了如下js代码:
var stateObj = { foo: 'bar' };
window.history.pushState(stateObj, 'page 2', 'bar.html');
假设接着执行了如下代码:
window.history.replaceState(stateObj, 'page 3', 'bar2.html');
这将会导致地址栏显示http://demo.com/bar2.html,但是浏览器并不会去加载bar2.html甚至不需要检查bar2.html是否存在。
假设现在用户重新导向了http://demo.com/other.html,然后点击了回退按钮,这里地址栏会显示http://demo.com/bar2.html,再次点击回退,地址栏显示foo.html完全跳过了bar.html
popstate事件
每当活动的历史记录项发生变化时,popstate
事件都会被传递给window对象。如果当前活动的历史记录项是被pushState
创建的,或者是由replaceState
改变的,那么popstate
事件的状态属性state
会包含一个当前历史记录状态对象的拷贝。
获取当前状态
页面加载时,或许会有个非null的状态对象。这是有可能发生的,你可以读取当前历史记录项的状态对象state,而不必等待popstate
事件,只需要这样使用history.state
属性:
var currentState = history.state;
三、window.onpopstate
window.onpopstate
是popstate
事件在window对象上的事件处理程序。
每当处于激活状态的历史记录条目发生变化时,popstate
事件就会在对应window对象上触发。如果当前处于激活状态的历史记录条目事由history.pushState()方法创建,或者由history.replaceState()方法修改过的,则popstate事件对象的state属性包含了这个历史记录条目的state对象的一个拷贝。
调用history.pushState()
或者history.replaceState()
不会触发popstate事件。popstate
事件只会在浏览器某些行为下触发,比如后退、前进或者在js中调用back()
等的方法。
当网页加载时,各浏览器对popstate
事件是否触发有不同的表现,Chrome和Safari会触发popstate
事件,而FireFox不会。
onpopstate语法
window.onpopstate = funcRef;
// funcRef是个函数名
popdtate事件
假如当前网页地址为http://demo.com/example.html,则运行下述代码后:
window.onpopstate = function(event) {
alert("location: " + document.location + ", state: " + JSON.stringify(event.state));
};
//绑定事件处理函数.
history.pushState({page: 1}, "title 1", "?page=1"); //添加并激活一个历史记录条目 http://demo.com/example.html?page=1,条目索引为1
history.pushState({page: 2}, "title 2", "?page=2"); //添加并激活一个历史记录条目 http://demo.com/example.html?page=2,条目索引为2
history.replaceState({page: 3}, "title 3", "?page=3"); //修改当前激活的历史记录条目 http://de..?page=2 变为 http://de..?page=3,条目索引为3
history.back(); // 弹出 "location: http://demo.com/example.html?page=1, state: {"page":1}"
history.back(); // 弹出 "location: http://demo.com/example.html, state: null
history.go(2); // 弹出 "location: http://demo.com/example.html?page=3, state: {"page":3}
即便进入了那些非pushState和replaceState方法作用过的(比如http://demo.com/example.html)没有state对象关联的那些网页, popstate
事件也仍然会被触发。
初始打开页面:
back一次:
back二次:
四、兼容性
Desktop
特性 | Chrome | Edge | FireFox | IE | Opera | Safari |
---|---|---|---|---|---|---|
replaceState, pushState | 5 | Yes | 4.0 | 10 | 11.50 | 5.0 |
history.state | 18 | Yes | 4.0 | 10 | 11.50 | 6.0 |
五、实例
参见《ajax与HTML5 history pushState/replaceState实例》
Ajax可以实现页面的无刷新操作——优点;但是,也会造成另外的问题,无法前进与后退。
当执行Ajax操作的时候,往浏览器history中塞入一个地址(使用pushState)(这是无刷新的);于是,返回的时候,通过URL或其他传参,我们就可以还原到Ajax之前的模样。
效果如图所示(源码地址):