使用 HTML5 History 新特性增强 Ajax 的体验
一. 场景再现
如大家熟知,Ajax 可以实现页面的无刷新操作,但会造成两个与普通页面操作(有刷新地改变页面)有着明显差别的问题—— URL 没有修改以及无法使用前进、后退按钮。例如常见的 Ajax 分页,在第一页点击第二页的链接,Ajax 分页完成后浏览器地址栏上显示的 URL 依然是第一页的 URL,使用后退按钮也无法回到第一页。url 的改变代表一个标识,在传统的网页体验中,内容的变更伴随 url 的改变,url 的改变、前进和后退按钮三者之间更加形成一种独特的导航体验,而 Ajax 破坏了这种体验难免会对用户造成不便。
当然,很早之前,机智的前端工程师们已经想好了解决办法,最为常见的,就是使用 hash ——在 URL 结尾添加形如"#xxx" 的标识,然后用 onhashchange 等方式监听 hash 值变化作出相应处理。
很麻烦?是的,因此,HTML5 History 中新增了几个可以优雅地解决这些问题的特性!
二. HTML5 History 的新特性
History 对象从 HTML4 开始引入,HTML5 中增加了 pushState, replaceState 两个方法,和 popstate 事件。下面作一些简单的介绍。
1. pushState()方法
pushState() 的作用是往历史记录的堆栈中压入一条记录,该方法有三个参数:
state object —— 一个对象,用于保存状态信息,当 popstate 事件被触发时,popstate 事件对象的 state 属性会包含相应的 state object 的拷贝。state object 的容量很小(Firefox 中强制为 640k),如果需要储存较大的数据,建议使用 localStorage 或 sessionStorage。
title —— 即被压入的历史记录的页面的标题,该属性暂时被所有浏览器忽略,实际开发时可以填空字符或一个简短的标题。
url —— 新的历史记录的地址,可以是相对路径或绝对路径,若为相对路径则以当前 url 为基址。
2. replaceState()方法
replaceState() 方法与 pushState() 方法类似,参数与 pushState() 也相同,但 replaceState() 方法会修改当前的历史记录而并非创建新的记录,因此在需要更新当前历史记录的 state object 或 URL 时,使用该方法会更加合适。
3. popstate 事件
popstate 事件会在激活的历史记录发生变化(如前进、后退、调用 pushState 或 replaceState 方法)时触发在 window 对象上。如上面所描述,如果被激活的历史记录由 pushState 创建或是被 replaceState 修改,则 popstate 事件的状态属性将包含相应的 state object 的拷贝,开发者可以在 popstate 的回调中调用这些之前保存在 state object 中的信息。
值得注意的是,Chrome 会在打开页面(包括第一次打开页面)以及页面刷新时产生 popstate 事件而 Firefox 则不会,这会为开发带来一些麻烦,但下面会给出解决方案。
4. 浏览器兼容
特性 | Chrome | Firefox (Gecko) | Internet Explorer | Opera | Safari |
---|---|---|---|---|---|
pushState, replaceState | 5 | 4.0 (2.0) | 10 | 11.50 | 5.0 |
history.state | 18 | 4.0 (2.0) | 10 | 11.50 | 6.0 |
三. 使用 History 的新特性增强 Ajax 体验
这里说的增强体验,实质就是要解决文章开头提到的两个问题 —— Ajax 翻页时不会修改 URL 以及前进、后退功能无效。
看了上面的方法,相信大家已经能想到基本的解决思路,无非就是在 Ajax 需求产生时 pushState 一条记录到历史记录中,然后在 popstate 的回调函数中作出相应的处理。当然,要想实际获得比较好的体验,要做的内容还是比较复杂的。下面以 Ajax 分页为例,提供一个完整思路:
- 每次使用 Ajax 获取新页面后,使用 pushState() 把新页面的 url 压入历史记录栈顶。
- 监听 popstate 事件,当用户前进或后退时在回调中根据前进或后退后的 URL 重新使用 Ajax 获取页面内容,实现 Ajax 前进与后退。
- Chrome 在打开页面以及刷新页面后会产生 popstate 事件,导致多余的 Ajax 请求,因此需要 popstate 中利用 url 作为判断用户是否在点击前进或后退功能,抑或是打开、刷新页面,若为前进或后退才触发 Ajax 获取相应内容。
基本代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
// 定义 Ajax 获取分页方法,这里不列出具体内容 function turnPage(url){ // ... } // 定义 popstate 的回调 if ( history.pushState ){ // 判断浏览器是否支持 pushState // currentState 用于记录 popstate 产生前的页面 url,页面加载时初始化 currentState 为当前链接 var currentState = window.location.href; window.addEventListener( ‘popstate‘ , function (event){ // _currentUrl 用于记录 popstate 产生时的页面 url var _currentUrl = window.location.href; /* 判断 currentState 和 _currentUrl 是否相同, * 若相同则表明这个 popstate 产生前后页面 url 没有变化, * 即页面是第一次加载页面或者被刷新,无需触发新的 Ajax 请求, * 若不同则表明 url 已改变,这是触发 Ajax 获取内容 */ if ( currentState != _currentUrl ){ console.log( ‘获取新分页‘ ); turnPage(_currentUrl); // 根据当前 url 重新获取分页内容 currentState = _currentUrl; // 更新 currentState } }); } // 绑定点击分页产生 Ajax 请求 // 点击分页链接,获取 url 为 currentLink,产生一次 Ajax 请求 turnPage(currentLink); // 调用 pushState() 压入新记录 if ( history.pushState ){ // 判断浏览器是否支持 pushState // 往历史记录中压入新记录,浏览器的地址栏上的 url 同时会被修改为新 url history.pushState( null , document.title, currentLink); currentState = window.location.href; // 分页时更新 currentState 为当前 url } |
本站已经根据上面的思路进行了 Ajax 分页的增强,具体的情况如下:
打开页面,url 显示为首页的 url
点击第二页链接,触发 Ajax 分页,在 Ajax 中调用 pushState() ,可以看到,url 已经被修改为第二页的 url
点击后退按钮,url 地址变为第一页,再在 popstate 的回调中触发 Ajax 获取第一页的内容。
这样,就做到了既吸收了局部刷新页面的好处同时保留传统的 url 导航体验。而对于不支持 pushState 的浏览器,可以使用渐进增强设计忽略这些浏览器,毕竟这是一个增强体验的功能;当然如果必须照顾那些浏览器,也可以采用 hash 的方案代替。
四. 现有的解决方案
如果需要直接的解决方案,可以参考这个封装好并且实现了跨浏览器兼容的库 —— History.js
本文由 Kayo Lee 发表,本文链接:http://kayosite.com/html5-history-improve-ajax.html
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。