history对象
history对象是window对象的属性,因此,每一个浏览器窗口、每一个标签页和每一个框架,都有自己的history对象(这个特别重要,一开始 学习的时候,我以为整个浏览器就只有一个history对象).出于安全方面的考虑,我们是没有办法知道用户访问过的页面列表,但是可以在不知道实际 url的情况下进行向前和向后跳转.
如下图表显示的是将会讲到的属性和方法
属性
length
保存历史记录的数量.对于加载到窗口标签页和框架中的第一个页面而言,history.length = 1(MDN上的文档说值为1,但是《JavaScript高级程序设计(第3版)》说它的值为0,我在firefox 38 和chrome 42下测试,history.length = 1)state
保存着传递给pushState()或则replaceState()的第一个参数的一个副本(查看《JavaScript权威指南(第6版)》644页的"结构性复制").这个值,也可以通过popstate事件的event.state属性访问到
方法
1. back()
在历史列表里后退一次,对于加载到窗口 标签页和框架中的第一个页面而言,也就是history.length=1时,调用此方法无效,也不会产生异常
back()=点击后退按钮=go(-1)
2. forward()
在历史列表里前进一次,对于加载到窗口 标签页和框架中的第一个页面而言,也就是history.length=1时,调用此方法无效,也不会产生异常
forward()=点击前进按钮=go(1)
3. go()
传入的数值是相对于当前历史记录的位置,数值越界的话无效,也不会产生异常,不传入任何参数或则传入参数为非数值时无效(《JavaScript高级程序设计(第3版)》在215页提到go()方法支持字符串参数,而MDN说IE才支持字符串参数)
4. pushState(x, y, z)
x是一个JavaScript对象(任何可以被序列化的对象,实际上,传递给x的对象的类型很广),
y是一个可选的字符串标题(我测试过,这个参数在firefox38和chrome42下看不到效果,MDN上说firefox会忽略这个参数)
z是一个URL,通常,可以用它来指定hash片段
来看如下的测试(在firebug中进行)
location.href
>> "http://localhost:63333/NumberGuessingGame/test.html"
location.pathname
>> "/NumberGuessingGame/test.html"
history.pushState({page:1},'','#page=1')
>> undefined
location.href
>> "http://localhost:63333/NumberGuessingGame/test.html#page=1"
location.pathname
>> "/NumberGuessingGame/test.html"
history.pushState({page:1},'','/#page=1')
>> undefined
location.href
>> "http://localhost:63333/#page=1"
location.pathname
>> "/"
可以看到z参数中的内容,原本只是简单的添加到localtion.pathname的末尾,但是,如果添加上/
字符,那么pathname就会被替换掉(注意,通过这个方法修改hash的值,不会触发hashchange事件)
5. replaceState(x, y, z)
与pushState()方法基本一致,但是它不是将新的历史记录添加到历史列表中,而是用新的历史记录替换掉当前的历史记录
事件
1. popstate
属性 | 类型 | 描述 |
---|---|---|
target 只读 | EventTarget | 浏览器上下文,就是window对象 |
type 只读 | DOMString | 事件的类型. |
bubbles 只读 | boolean | 事件正常情况下冒泡吗? |
cancelable 只读 | boolean | 可以取消事件吗? |
state 只读 | any | 当前历史条目的state对象(如果设置了的话). |
当当前的历史条目改变的时候,就会触发popstate事件.如果历史条目是通过pushState()或则replaceState()来设置的,那么事件处理程序中event.state会包含一条历史条目的state对象的副本
但是pushState()和replaceState()并不会触发popstate和hashchange事件,实际上, 是通过按下前进后退按钮或则history.back()来触发的.
请看如下测试
测试页面为:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title></title>
</head>
<body>
<script>
"use strict";
window.onpopstate = function () {
console.log('触发popstate');
};
window.onhashchange = function () {
console.log('触发hashchange');
};
</script>
</body>
</html>
测试结果
history.pushState({page:1},'','#page=1')
>> undefined
location.href
>> "http://localhost:63333/NumberGuessingGame/test.html#page=1"
# 很明显,虽然url改变了,但是没有触发hashchange和popstate事件
history.back()
>> 触发popstate
>> undefined
>> 触发hashchange
location.href
>> "http://localhost:63333/NumberGuessingGame/test.html"
#这下触发了,接下来测试是用来location对象来操作
location.hash='#page=2'
>> 触发popstate
>> "#page=2"
>> 触发hashchange
location.href
>> "http://localhost:63333/NumberGuessingGame/test.html#page=2"
通过测试,可以看到popstate和hashchange触发条件的不同,而且,popstate先于hashchange被触发.
实际上,通 过pushState()和replaceState()修改了URL甚至不会产生相应的跳转(例如,通过修改location对象的属性值(除了 hash属性),都会立即导致浏览器跳转到相应的URL),在这种情况下,想要实现跳转只能刷新一下页面,或则回退一次历史记录在前进一次历史记录.
管理历史记录的两种方法
利用location.hash属性和hashchange事件
利用pushState()或则replaceState()方法和popstate事件
那么,我们该用哪种方法呢?使用第一种的话,确实比较简单,而且浏览器在HTML5标准化之前就支持这种技术了.我觉得差别不大,更看个人喜好和需求,更觉第二种方法更重量级一些.接下来,我展示一下通过两种不同的技术实现一个管理状态的功能吧!
加入,我们有一个如下的对象需要保存
{
bookName: 'SegmentFault',
author: 'tcstory',
date: '2015/05/17'
}
通过第一种方法来保存:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title></title>
</head>
<body>
<script>
"use strict";
var book = {
bookName: 'SegmentFault',
author: 'tcstory',
date: '2015/05/17'
};
var saveBook = function () {
var _temp;
_temp = 'bookName=' + book.bookName;
_temp = _temp + '&author=' + book.author;
_temp = _temp + '&date=' + book.date;
location.hash =_temp;
};
</script>
</body>
</html>
打开页面后,在控制台进行如下测试
saveBook()
>> undefined
location.href
>> "http://localhost:63333/NumberGuessingGame/test.html#bookName=SegmentFault&author=tcstory&date=2015/05/17"
可以看到,保存的内容在URL中体现出来了.
现在换第二种方法:
使用的页面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title></title>
</head>
<body>
<script>
"use strict";
var book = {
bookName: 'SegmentFault',
author: 'tcstory',
date: '2015/05/17'
};
var saveBook = function () {
history.replaceState(book, '', '');
};
</script>
</body>
</html>
在终端中进行如下测试
saveBook()
>> undefined
history.state
>> Object { bookName="SegmentFault", author="tcstory", date="2015/05/17"}
额,我更喜欢后面一种~!感觉更直观~!
参考资料:
https://developer.mozilla.org/en-US/docs/Web/API/History
https://developer.mozilla.org/en-US/docs/Web/Events/popstate
update: 2015.06.13
由于调用pushState()和replaceState()不会主动触发任何事件,这就导致了使用这两个方法难以保证状态的一致性,比如说,你以为你自己在这个页面,但是由于调用了那两个方法,你自己实际上是在另一个页面了,你却不知道.所以,我感觉这两个方法没有多大的用处.