H5系列之History(必知必会)
目录
- 理解History Api的使用方式
- 目的是为了解决哪些问题
作用:ajax获取数据时,可以改变历史记录,从而可以使用浏览器的后退和前进。
【】
【01】History 对象
【】Window对象的history属性引用的是该窗口的History对象。
【】History 对象包含用户(在浏览器窗口中)访问过的 URL。
History 对象是 window 对象的一部分,可通过 window.history 属性对其进行访问。
注释:没有应用于 History 对象的公开标准,不过所有浏览器都支持该对象。
History接口允许操作浏览器的曾经在标签页或者框架里访问的会话历史记录。
History 对象最初设计来表示窗口的浏览历史。
但出于隐私方面的原因,History 对象不再允许脚本访问已经访问过的实际 URL。
history对象保存着用户上网的历史记录,从窗口被打开的那一刻算起。
因为history是window对象的属性,因此每个浏览器窗口、每个标签页乃至每个框架,都有自己的history对象与特定的window对象关联。
出于安全方面的考虑,开发人员无法得知用户浏览过的URL。(如果允许,则任意脚本都可以窥探你的浏览历史。)
不过,借由用户访问过的页面列表,同样可以在不知道实际URL的情况下实现后退和前进。
【】back()和forward(),页面通常是从浏览器缓存中加载,而不是重新要求服务器发送新的请求。
【】虽然history并不常用,但在创建自定义的“后退”和“前进”按钮,以及检测当前页面是不是用户历史记录中的第一个页面时,还是必须使用它。
当页面的URL改变时,就会生成一条历史记录。
在IE8及更高版本、Opera、Firefox、Safari 3及更高版本以及Chrome中,这里所说的改变包括URL中hash的变化(因此,设置location.hash会在这些浏览器中生成一条新的历史记录)。
【01】
History API
Web应用通常都是动态地生成或载入页面内容,并在无须刷新页面的情况下就显示新的应用状态。
如果想要提供用户能够通过浏览器的“后退”和“前进”按钮,直观地切换应用状态,像这类应用就必须自己处理应用的历史记录管理。
【】
Ajax有一个问题一直困扰着开发者,就是网页状态无法被添加到历史记录中,这意味着用户不能通过浏览器的“前进”和“后退”按钮前进或者退回到某个状态。
【】可以在历史记录中添加状态或改变状态
【】可以使用JavaScript来部分更新页面,又能更新地址栏和浏览器历史记录。
【】使用History API实现了地址栏改变而页面不跳转的效果。
【】而通过状态管理API,能够在不加载新页面的情况下改变浏览器的URL。
【】在使用HTML5的状态管理机制时,请确保使用pushState()
创造的每一个“假”URL,在Web服务器上都有一个真的、实际存在的URL与之对应。否则,单击“刷新”按钮会导致404错误。
【】可能犯的一个错误
如果你遇到使用History API没有起作用的情况,可以尝试用try catch来试着捕获异常。
你很可能会发现异常显示为SECURITY_ERR:DOM Exceptin 18。
这是由于History API采用同源控制策略来保证安全性,避免恶意网站修改用户其他网站的历史记录。
出现这种情况的原因是你没有正确设置URL或者使用本地页面,History API认为你修改的是另外一个域名的历史记录。
先来看看"history"接口的详细方法:
interface History {
readonly attribute long length;
readonly attribute any state;
void go(optional long delta);
void back();
void forward();
void pushState(any data, DOMString title, optional DOMString? url = null);
void replaceState(any data, DOMString title, optional DOMString? url = null);
};
HTML5定义了两种用于历史记录管理的机制。
其中比较简单的历史记录管理技术就是利用location.hash和hashchange事件。
设置location.hash属性会更新显示在地址栏中的URL,同时会在浏览器的历史记录中添加一条记录。
hash属性设置URL的片段标识符,通常是用于指定要滚动到的文档中某一部分的ID。
但是location.hash不一定非要设置为一个元素的ID:
它可以设置成任何的字符串。
如果能够将应用状态编码成一个字符串,就可以使用该字符串作为片段标识符。
设置了location.hash属性后,接下来要实现允许用户通过“后退”和“前进”按钮来切换不同的文档状态。这个时候,应用必须要想办法检测状态变化,以便它能够读取出存储在片段标识符中的状态并相应地更新自己的状态。
支持HTML5的浏览器一旦发现片段标识符发生了改变,就会在Window对象上触发一个hashchange事件。
这样,在支持hashchange事件的浏览器中,就可以通过设置window.onhashchange为一个处理程序函数,使得每次由于切换历史记录导致片段标识符变化的时候,都会调用该处理程序函数。
当调用该处理程序函数的时候,就可以对location.hash的值进行解析,然后使用该值包含的状态信息来重新显示应用。
为了提高Web页面的响应速度,越来越多的开发者开始采用单页面结构(single-page application)的解决方案。
所谓的单页面结构就是指多个页面间切换时,不刷新当前整个页面,更新页面展示数据,并且相应地改变地址栏中的url,以使用户可以分享这个url。
如果你使用chrome或者firefox等浏览器访问"github.com、plus.google.com"等网站时,细心的你会发现页面之间的点击是通过ajax异步请求的,
同时页面的URL发生了改变。并且能够很好的支持浏览器前进和后退。
【】如果窗口包含多个子窗口(比如<iframe>元素),子窗口的浏览历史会按时间顺序穿插在主窗口的历史中。
这意味着在主窗口调用history.back()(举例)可能会导致其中一个子窗口往回跳转到前一个显示的文档,但主窗口保留当前状态不变。
【】现代Web应用可以不通过载入新文档而动态地改变自身内容(这么做可能希望用户能用“后退”和“前进”按钮在这些动态创建的应用状态之间进行跳转。)
【】HTML5之前的历史管理是个更复杂的难题。
应用程序必须要在窗口浏览历史中创建一个新的条目来管理自身的历史记录,用历史条目关联自身的状态信息,判断什么时候用户使用了“后退”按钮来移动到不同的历史条目,联合那个条目获取状态信息,并且重新创建应用程序之前的状态。
【】一种方式是用隐藏的<iframe>来保存状态信息并在浏览器的历史中创建条目。
为了创建新的历史条目,需要用Document对象的open()和write()方法动态地把一个新文档写入这个隐藏的窗体。
不管怎样,文档内容应该包含重新创建应用状态所需要的状态信息。当用户单击“后退”按钮,隐藏的窗体的内容会改变。
在HTML5之前,没有生成事件来通知你这个改变,因此,为了检测用户是否单击了“后退”按钮,可能要用setInterval()每秒对隐藏的窗体检测两到三次,来看它是否改变了。
【】在实际工作中,在那些需要以前的HTML5历史管理的项目中,开发者通常会使用一些现成的解决方案。
很多JavaScript框架都实现了这种功能。比如,jQuery有history插件,另外也有些单独的管理历史记录的类库。
【03】兼容性
Firefox 4+、Safari 5+、Opera 11.5+和Chrome8+。
IE10支持。
为了用变成方式确定浏览器是否支持这个API,可以用下面的一行代码检验:
return!!(window.history&&history.pushState);
如果你是用的现代浏览器,可以用下面的代码:
if(Modernizr.history){
如果你的浏览器不支持History API,可以使用history.js代替。
**
属性
【02】
length 属性
定义和用法
返回一个整数,该整数表示会话历史中元素的数目,包括当前加载的页。例如,在一个新的选项卡(浏览器)加载的一个页面中,这个属性返回1。
length 属性声明了浏览器历史列表中的元素数量。
保存着历史记录的数量。这个数量包括所有历史记录,即所有向后和向前的记录。对于加载到窗口、标签页或框架中的第一个页面而言,history.length等于0。
通过像下面这样测试该属性的值,可以确定用户是否一开始就打开了你的页面。
if (history.length == 0){
}
注释:IE 6 和 Opera 9 以 0 开始,而 Firefox 1.5 以 1 开始。
语法
实例
<html>
<body>
<scripttype="text/javascript">
console.log(history.length);
</script>
</body>
</html>
【】魔芋:chrome以1开始。
【02】state
History.state:
返回一个表示历史堆栈顶部的状态的值。这是一种可以不必等待popstate事件而查看状态而的方式
用于存储history.pushState(data)和history.repalceState(data)的data数据。
不同浏览器的读写权限不一样。
history.pushState({name:"魔芋"},"moyu","moyu.html");
console.log(history.state);
【03】scrollRestoration
History.scrollRestoration:
允许Web应用程序在历史导航上显示地设置默认滚动恢复行为。此属性可以是自动的auto或者手动的manual
【04】forward() 方法
调用该方法的效果等价于点击前进按钮或调用 history.go(1)。
语法
定义和用法
forward() 方法可加载历史列表中的下一个 URL。
实例
下面的例子可以在页面上创建一个前进按钮:
<html>
<head>
<scripttype="text/javascript">functiongoForward()
{
window.history.forward()
}
</script>
</head>
<body>
<inputtype="button"value="Forward"onclick="goForward()" />
</body>
</html>
【03】
back() 方法
定义和用法
back() 方法可加载历史列表中的前一个 URL(如果存在)。
调用该方法的效果等价于点击后退按钮或调用 history.go(-1)。
等同于点击浏览器的后退按钮。
语法:
例子:
<html>
<head>
<scripttype="text/javascript">functiongoBack()
{
window.history.back()
}
</script>
</head>
<body>
<inputtype="button"value="Back"onclick="goBack()" />
</body>
</html>
【05】go() 方法
定义和用法
go() 方法可加载历史列表中的某个具体的页面。
语法
负数表示向后跳转(类似于单击浏览器的“后退”按钮),正数表示向前跳转(类似于单击浏览器的“前进”按钮)。
history.go(-2);
go(1) ;
go(-1) ; /后退一个;
说明
URL 参数使用的是要访问的 URL,或 URL 的子串。而 number 参数使用的是要访问的 URL 在 History 的 URL 列表中的相对位置。
使用go()方法可以在用户的历史记录中任意跳转,可以向后也可以向前。
这个方法接受一个参数,表示向后或向前跳转的页面数的一个整数值。
当整数参数超出界限时,例如:如果当前页为第一页,前面已经没有页面了,我传参的值为-1,那么这个方法没有任何效果也不会报错。
调用没有参数的go()方法或者不是整数的参数时也没有效果。
也可以给go()方法传递一个字符串参数,此时浏览器会跳转到历史记录中包含该字符串的第一个位置——可能后退,也可能前进,具体要看哪个位置最近。
如果历史记录中不包含该字符串,那么这个方法什么也不做,例如:
history.go("wrox.com");
history.go("nczonline.net");
其中history.go(0)
相当于刷新当前页面。
实例
下面例子会加载历史列表中的前一个页面:
<html>
<head>
<scripttype="text/javascript">functiongoBack()
{
window.history.go(-1)
}
</script>
</head>
<body>
<inputtype="button"value="Back"onclick="goBack()" />
</body>
</html>
【01】pushState()
用于向history对象添加当前页面的记录,并且改变浏览器地址栏的URL。
(魔芋:就是在历史记录里加入新的url地址,浏览器地址栏也会变成新的相对URL)
它有3个参数,分别是state对象、可选的新状态的标题(普通的文本字符串)以及可选参数目标URL。
state是一个JavaScript对象,记录要插入到历史记录条目的相关信息。
该对象可以是任何能够通过JSON.stringify()方法转换成相应字符串形式的对象,也可以是其他类似Date和RegExp这样特定的本地类型。
而第一个参数则应该尽可能提供初始化页面状态所需的各种信息。state会在onpopstate事件触发时作为参数传递过去。
该方法的第二个可选参数是一个可选的标题,浏览器可以使用它(比如,在一个<Back>菜单中)来标识浏览历史记录中保存的状态。
但是,浏览器并不会真的向服务器发送请求,即使状态改变之后查询location.href
也会返回与地址栏中相同的地址。
另外,第二个参数目前还没有浏览器实现,因此完全可以只传入一个空字符串,或者一个短标题也可以。
该方法的第三个参数是一个可选的URL,表示当前状态的位置。
相对的URL都是以文档的当前位置为参照,通常该URL只是简单地指定URL(诸如#state)这样的hash(或者“片段标识符”)部分。
将一个URL和状态关联,可以允许用户将应用的内部状态作为书签添加到浏览器中,并当在URL中包含足够信息的时候,应用可以在从书签中载入的时候就恢复它的状态。
缺省为当前页面地址。
新的网址,必须与当前页处在同一个域。浏览器的地址栏将显示这个网址。
pushState()的示例代码如下:
history.pushState(javascriptObj,"some string","url");
例子:
history.pushState({username: "html5"}, "user account", "user.html");
注意 pushState()带来地址栏的变化不会触发 hash
(用过锚的开发者应该了解,URL #号后面的部分就是hash。改变hash的值后,页面不会跳转到新页面,也不会刷新)跳转。
moyu:
我在b.html里面加上了
<script>
history.pushState({username: "html5"}, "user account", "c.html");
console.log(history);
</script>
结果:打开的是a.html页面,但是地址栏为b.html。
不会跳转到b.html。也不会检查b.html
是否存在,它只是成为浏览历史中的最新记录。
打开谷歌浏览器的历史记录,你会发现有2条数据了。
pushState
方法不会触发页面刷新,只是导致history
对象发生变化,地址栏会有反应。
如果pushState
的url
参数,设置了一个新的锚点值(即hash
),并不会触发hashchange
事件。如果设置了一个跨域网址,则会报错。
history.pushState({title: 'twitter'}, 'twitter', 'https://twitter.com/moyu')
上面代码中,pushState
想要插入一个跨域的网址,导致报错。
这样设计的目的是,防止恶意代码让用户以为他们是在另一个网站上。
在Safari和Chrome中,传递给pushState()
或replaceState()
的状态对象中不能包含DOM元素。
而Firefox支持在状态对象中包含DOM元素。
Opera还支持一个history.state
属性,它返回当前状态的状态对象。
【】pushState()方法接受一个状态对象并为该对象创建一份私有副本。
这是对一个对象进行深拷贝或者深复制:它会递归地复制所有嵌套对象或者数组的内容。
HTML5标准将这类复制称为“结构性复制”(structured clone)。
创建一个结构性复制的过程就好比是将一个对象传递给JSON.stringify()方法,然后再将结果字符串传递给JSON.parse()方法。
但是JSON只支持JavaScript的基础类型和对象以及数组。
在HTML5标准中提到,结构性复制算法必须还能够复制Date对象、RegExp对象、ImageData对象(来自<canvas>元素)、FileList对象、File对象以及Blob对象。
但是在结构性复制算法中会显式排除JavaScript中的函数和错误以及绝大部分诸如窗口、文档、元素等这类宿主对象。
或许还不会存储文件或者图片数据作为历史状态的一部分,但是结构性复制还被其他一些HTML5相关的标准用到。
【02】replaceState()
replaceState()类似于pushState(),只是将当前页面状态替换为新的状态,示例代码如下:
调用这个方法不会在历史状态栈中创建新状态,只会重写当前状态。
history.replaceState({username: "html5" }, "user account", "user.html");
截图:
【03】onpopstate事件
【01】另外,History API还提供了onpopstate事件,该事件在窗口历史记录改变时被触发。
当用户单击浏览器的后退和前进按钮时触发该事件。
在事件处理函数中,读取触发触发事件的事件对象的state属性值。该属性为pushState()的第一个参数值。
当你浏览会话历史记录时,不管你是点击前进或者后退按钮,还是使用history.go和history.back方法,popstate都会被触发。
pushState()
会创建新的历史状态,会发现“后退”按钮也能使用了。
按下“后退”按钮,会触发window
对象的popstate
事件。
【】每当同一个文档的浏览历史(即history
对象)发生变化时,就会触发popstate
事件。
需要特别注意的是,仅仅调用history.pushState()
或history.replaceState()
方法时,并不会触发popstate
事件。
只有用户点击浏览器后退按钮和前进按钮,或者使用JavaScript调用history.back()
、history.forward()
和history.go()
方法时才会触发。
另外,popstate
事件只针对同一个文档,如果浏览器历史的切换,导致加载不同的文档,该事件也不会被触发。
window的属性事件么?魔芋
示例代码如下:
window.onpopstate = function(event){
console.log(event.state);
}
也可以这样使用:
window.addEventListener('popstate', function(event) {
console.log('location: ' + document.location)
console.log('state: ' + JSON.stringify(event.state))
})
演示在用JavaScript更新页面内容时通过History API来改变页面地址和历史记录。
<div id="click-item"></div>
<div id="result-item"></div>
<script type="text/javascript">
var clickItem = document.getElementById('click-item');
var resultItem = document.getElementById('result-item');
clickItem.onclick = function(){
resultItem.innerHTML = 'clicked!';
history.pushState({note: 'set result'}, '', 'result.html');
}
</script>
单击click-item元素时,浏览器页面地址将会变为result.html,同时浏览器的历史记录里也会出现此页面的访问记录。
popstate
事件的事件对象有一个state
属性,这个属性就包含着当初以第一个参数传递给pushState()
的状态对象。
popstate
事件发生后,事件对象中的状态对象(event.state
)是当前状态。
EventUtil.addHandler(window, "popstate", function(event){
var state = event.state;
if (state){
processState(state);
}
});
得到这个状态对象后,必须把页面重置为状态对象中的数据表示的状态(因为浏览器不会自动为你做这些)。
记住,浏览器加载的第一个页面没有状态,因此单击“后退”按钮返回浏览器加载的第一个页面时,event.state
值为null
。
【】
注意:页面第一次加载的时候,在load
事件发生后,Chrome和Safari浏览器(Webkit内核)会触发popstate
事件,而Firefox和IE浏览器不会。