一. 场景再现

如大家熟知,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 的体验(转)的更多相关文章

  1. HTML5 history新特性pushState、replaceState

    DOM中的window对象通过window.history方法提供了对浏览器历史记录的读取,让你可以在用户的访问记录中前进和后退. 从HTML5开始,我们可以开始操作这个历史记录堆栈. 1.Histo ...

  2. HTML5 history新特性pushState、replaceState,popstate

    http://blog.csdn.net/tianyitianyi1/article/details/7426606 https://developer.mozilla.org/zh-CN/docs/ ...

  3. 浅谈HTML5的新特性

    2014年10月29日,W3C宣布,经过接近8年的艰苦努力,HTML5标准规范终于制定完成. HTML5将会取代1999年制定的HTML 4.01.XHTML 1.0标准,使网络标准达到符合当代的网络 ...

  4. Atitti html5 h5 新特性attilax总结

    Atitti html5 h5 新特性attilax总结 Attilax觉得不错的新特性 3.语义Header和Footer (The Semantic Header and Footer) 8.占位 ...

  5. Atitit html5.1 新特性attilax总结

    Atitit html5.1 新特性attilax总结 9. 嵌入 header 和 footer1 7. 校验表单1 6. 浏览器的上下文菜单2 1. 响应式图像2 Attilax觉得还不错的心特性 ...

  6. HTML5的新特性:范围样式,又叫做<style scoped>

    Chromium 最近实现了一个HTML5的新特性:范围样式,又叫做<style scoped> .开发者可以通过为根元素设定一个添加了scoped属性的style标签,来限制样式只作用于 ...

  7. C# 9 新特性 —— 增强的 foreach

    C# 9 新特性 -- 增强的 foreach Intro 在 C# 9 中增强了 foreach 的使用,使得一切对象都有 foreach 的可能 我们来看一段代码,这里我们试图遍历一个 int 类 ...

  8. C# 9 新特性 —— 增强的模式匹配

    C# 9 新特性 -- 增强的模式匹配 Intro C# 9 中进一步增强了模式匹配的用法,使得模式匹配更为强大,我们一起来了解一下吧 Sample C# 9 中增强了模式匹配的用法,增加了 and/ ...

  9. jdk1.5出现的新特性---->增强for循环

    import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; ...

随机推荐

  1. nginx缓存和flask_cache

    1.使用flask_cache的缓存功能simple模式时,直接启用可以使用,但是如果中间使用nginx代理时,就没有效果了 2.那就直接使用nginx缓存机制 http://blog.csdn.ne ...

  2. 【BZOJ】【3437】小P的牧场

    DP/斜率优化 斜率优化基本题……等等,好像就没啥变化啊= = 嗯目测这题跟仓库建设差不多?写题的时候倒是没想这么多……直接推了公式. $$f[i]=min\{f[j]+cal(j,i)+a[i]\} ...

  3. convert-a-number-to-hexadecimal

    https://leetcode.com/problems/convert-a-number-to-hexadecimal/ // https://discuss.leetcode.com/topic ...

  4. iOS开发-xCode6(iOS 8)中应用程序图标和启动页面设置

    iOS8中设置应用程序图标跟之前没有什么变化,命名规则不变,不过至于设置启动页面,网上给的方式很多都是模棱两可的,东平西凑总算是把启动页面的图片设置成功了,iOS设置启动图片有两种方式一种是Launc ...

  5. Best Time to Buy and Sell Stock leetcode java

    题目: Say you have an array for which the ith element is the price of a given stock on day i. If you w ...

  6. Android GUI之Window、WindowManager

    通过前几篇的文章(查看系列文章:http://www.cnblogs.com/jerehedu/p/4607599.html#gui ),我们清楚了Activity实际上是将视图的创建和显示交给了Wi ...

  7. Python3 写Windows Service服务程序

    用Python开发Windows Service,用Python来做这个事情必须要借助第三方模块pywin32,下载路径:https://pypi.org/project/pywin32/#files ...

  8. LA 4728 Square ,旋转卡壳法求多边形的直径

    给出一些正方形.让你求这些正方形顶点之间的最大距离的平方. //返回点集直径的平方 int diameter2(vector<Point> & points) { vector&l ...

  9. Redis自学笔记—PHP

    connect 实例连接到一个Redis. $redis = new redis(); $result = $redis->connect('127.0.0.1', 6379); var_dum ...

  10. 【转】Arp的攻防实战

    坦白讲,我是个小白,也就是各大黑客和安全厂商所说的目标群体,前者是攻击我,后者有可能保护我.但是无论如何,这次后者应该也很无能为力了——我遭遇到了传说中的Arp攻击. 在连续一周的时间里,我发现我无法 ...