使用 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
使用 HTML5 History 新特性增强 Ajax 的体验(转)的更多相关文章
- HTML5 history新特性pushState、replaceState
DOM中的window对象通过window.history方法提供了对浏览器历史记录的读取,让你可以在用户的访问记录中前进和后退. 从HTML5开始,我们可以开始操作这个历史记录堆栈. 1.Histo ...
- HTML5 history新特性pushState、replaceState,popstate
http://blog.csdn.net/tianyitianyi1/article/details/7426606 https://developer.mozilla.org/zh-CN/docs/ ...
- 浅谈HTML5的新特性
2014年10月29日,W3C宣布,经过接近8年的艰苦努力,HTML5标准规范终于制定完成. HTML5将会取代1999年制定的HTML 4.01.XHTML 1.0标准,使网络标准达到符合当代的网络 ...
- Atitti html5 h5 新特性attilax总结
Atitti html5 h5 新特性attilax总结 Attilax觉得不错的新特性 3.语义Header和Footer (The Semantic Header and Footer) 8.占位 ...
- Atitit html5.1 新特性attilax总结
Atitit html5.1 新特性attilax总结 9. 嵌入 header 和 footer1 7. 校验表单1 6. 浏览器的上下文菜单2 1. 响应式图像2 Attilax觉得还不错的心特性 ...
- HTML5的新特性:范围样式,又叫做<style scoped>
Chromium 最近实现了一个HTML5的新特性:范围样式,又叫做<style scoped> .开发者可以通过为根元素设定一个添加了scoped属性的style标签,来限制样式只作用于 ...
- C# 9 新特性 —— 增强的 foreach
C# 9 新特性 -- 增强的 foreach Intro 在 C# 9 中增强了 foreach 的使用,使得一切对象都有 foreach 的可能 我们来看一段代码,这里我们试图遍历一个 int 类 ...
- C# 9 新特性 —— 增强的模式匹配
C# 9 新特性 -- 增强的模式匹配 Intro C# 9 中进一步增强了模式匹配的用法,使得模式匹配更为强大,我们一起来了解一下吧 Sample C# 9 中增强了模式匹配的用法,增加了 and/ ...
- jdk1.5出现的新特性---->增强for循环
import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; ...
随机推荐
- Java网络编程技术1
1. Java网络编程常用API 1.1 InetAddress类使用示例 1.1.1根据域名查找IP地址 获取用户通过命令行方式指定的域名,然后通过InetAddress对象来获取该域名对应的IP地 ...
- linux C 多线程/线程池编程 同步实例
在多线程.线程池编程中经常会遇到同步的问题. 1.创建线程 函数原型:int pthread_create(pthread_t *thread, const pthread_attr_t *attr, ...
- Android数据存储之SQLite
SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它的设计目标是嵌入式的,而且目前已经在很多嵌入式产品中使用了它,占用资源非常少,只有几百KB内存.支持Windows/Linux ...
- Bootstrap学习js插件篇之滚动监听
1.滚动监听 案例 滚动监听插件可以根据滚动条的位置自动更新所对应的导航标记.Bootstrap中文网左侧就是一个滚动监听的例子. 代码段: <nav id="navbar-examp ...
- IOS Xib使用——Xib表示局部界面
Xib文件是一个轻量级的用来描述局部界面的文件,在之前的文章讲解了为控制器添加Xib文件,本节主要讲解一下通过xib文件表示局部界面. <一> 创建Xib文件 Xib文件创建的时候是选择U ...
- ListView 设置列对齐方式
<ListView.Resources> <Style TargetType="ListViewItem"> <Setter Property=&qu ...
- [Backbone]2. More detail in Models
Our Appointment model doesn't seem too useful yet. Add two default attributes, title as the string & ...
- ZOJ 3587 扩展KMP
思路:这题确实大帝做得非常机智!字符串先求最长前缀,反的字符串再求一次最长前缀.然后就能够搞了. 每一个子串出现的次数就是最长前缀的次数嘛! #pragma comment(linker, " ...
- jquery设置按钮disabled
通常我们设置disabled属性会用attr和removeAttr,但是今天我们不讨论这个方式 我们讨论是的prop jQuery的定义是:获取在匹配的元素集中的第一个元素的属性值. 随着一些内置属性 ...
- JS中关于in运算符的问题
转自:http://bbs.bccn.net/thread-412608-1-1.html in运算符 in运算符虽然也是一个二元运算符,但是对运算符左右两个操作数的要求比较严格.in运算符要求第1个 ...