HTML5 History API提供了一种功能,能让开发人员在不刷新整个页面的情况下修改站点的URL。这个功能很有用,例如通过一段JavaScript代码局部加载页面的内容,你希望通过改变当前页面的URL来反应出页面内容的变化,这时该功能可以派上用场。

  举个例子,当用户从首页进入帮助页面时,我们通过Ajax来加载帮助页面的内容。然后这个用户又转到产品页面,我们需要再一次通过Ajax请求来替换页面的内容。当用户想分享页面的URL时,通过History API,我们可以改变页面的URL来反应内容的修改,这样不管是用户分享还是保存的URL都能和页面的内容对应起来。

基本知识

  要查看这个API提供了哪些功能非常简单,打开浏览器的Developer Tools工具面板,然后在console中输入history。如果你的浏览器支持History API,你将会看到这个对象下面附带了很多方法。

  注意其中的pushStatereplaceState这两个方法。我们可以在console中进行一些简单的测试,来看看当我们使用这两个方法时URL会发生什么变化。稍后我们将分析这两个方法中的所有参数,现在我们只需要关注最后一个参数:

history.replaceState(null, null, 'hello');

  上面代码中的replaceState方法改变了当前页面的URL,在后面添加了一个'/hello'。不过并没有发出任何request请求,当前窗口仍然停留在之前的页面。不过这里有个问题,当你点击浏览器的后退按钮时,页面并不会回退到我们通过replaceState方法修改之前的那个URL,而是直接回退到了上一个页面(即我们进入到这个页面之前的那个页面)。这是因为replaceState方法不会修改浏览器的history,它只是简单地替换了地址栏中的URL。

  要解决这个问题我们需要使用pushState方法:

history.pushState(null, null, 'hello');

  现在再点击浏览器的后退按钮,你会发现它和你预想的效果一样。因为pushState方法将我们传给它的URL添加到浏览器的history中,从而改变了浏览器的history。假如我们将另外一个完整的站点URL传递给它会发生什么情况呢?例如我们在baidu.com的首页进行测试,然后在console中输入下面的内容。

history.pushState(null, null, 'https://twitter.com/hello');

  浏览器会报错。因为传递给pushState方法的URl必须和当前页面的URL属于同一个源(即不能跨域),否则会有很大的安全漏洞,开发人员可能会借用该功能来欺骗用户,让他们觉得自己是在访问一个完全不同的站点,而事实并非如此。

  来看看传递给pushState方法的所有参数:

history.pushState([data], [title], [url]);
  1. 第一个参数用来传递我们需要的数据,当页面的状态发生变化时我们可以接收到该数据。如用户点击浏览器的后退和向前按钮。需要注意的是在Firefox中只允许传递最多640K的数据。
  2. 第二个参数title是一个字符串,不过截止到目前,几乎所有的浏览器都忽略该参数。
  3. 最后一个参数是我们想要替换的URL。

简单回顾一下

  这些History API最主要的功能就是不重新加载页面。以往我们只能通过改变window.location的值来修改当前页面的URL,不过这会导致整个页面被重新加载。如果你修改的只是URL中的hash,则不会导致页面被刷新。

  使用旧的hashbang方法可以改变页面的URL而不刷新页面。著名的Twitter就是使用的该方法,不过也广受诟病,毕竟hash在location中并不被作为一个真正的资源来对待。

  作为History API的早期支持者,Twitter后来抛弃了传统的hashbang方法。在2012年,Twitter的团队介绍了他们的新方法,并列出了其中的一些问题同时还详细地介绍了各浏览器应该如何实现该规范。

一个使用pushState和Ajax的例子

https://css-tricks.com/examples/State/

  在该示例中,我们希望用户通过我们的网站找到电影捉鬼敢死队(一部美国电影)中的演员。当用户选择一个图片时,我们需要在下方显示该演员对应的文字描述,同时给该图片一个被选中的效果。当点击后退按钮时,页面应该切换到上一个被选中的图片状态,同时图片下方的文字也要一并切换。当点击前进按钮时也一样。

  这里有一个效果图:

  这个示例的HTML代码非常简单:div.gallery中包含了所有的链接,每个链接里有一个图片。接下来我们放置了一个空的div.content,用来存放当演员图片被点击时显示在下放的文字。

<div class="gallery">
<a href="https://cdn.css-tricks.com/peter.html">
<img src="bill.png" alt="Peter" class="peter" data-name="peter">
</a>
<a href="https://cdn.css-tricks.com/ray.html">
<img src="ray.png" alt="Ray" class="ray" data-name="ray">
</a>
<a href="https://cdn.css-tricks.com/egon.html">
<img src="egon.png" alt="Egon" class="egon" data-name="egon">
</a>
<a href="https://cdn.css-tricks.com/winston.html">
<img src="winston.png" alt="Winston" class="winston" data-name="winston">
</a>
</div> <p class="selected">Ghostbusters</p>
<p class="highlight"></p> <div class="content"></div>

  如果没有JavaScript该页面仍然可以正常工作,点击图片可以跳转到对应的页面,然后点击后退按钮也可以回到之前的页面。这是为了考虑页面的可访问行和优雅降级。

  接下来我们要添加JavaScript代码了。我们通过event propagation给div.gallery元素中的每一个link添加一个事件处理程序,像这样:

var container = document.querySelector('.gallery');

container.addEventListener('click', function(e) {
if (e.target != e.currentTarget) {
e.preventDefault();
// e.target is the image inside the link we just clicked.
}
e.stopPropagation();
}, false);

  在if语句中,我们获取到被选中图片的data-name属性的值,然后将'.html'添加到后面拼成一个要访问的页面地址,并将其作为第三个参数传递给pushState方法(不过在真实的例子中我们可能会在Ajax请求成功之后才会去修改URL)。

var data = e.target.getAttribute('data-name'),
url = data + ".html";
history.pushState(null, null, url); // 此处更改当前的classes样式
// 然后使用data变量的值更新
// 并通过Ajax请求.content元素的内容
// 最后再更新当前文档的title

(当然,此处我们也可以直接使用link的href属性的值)

  我将真实代码中的内容都替换成注释了,这样我们可以只关注pushState方法的使用。

  现在我们点击图片,URL和Ajax请求的内容会被自动更新,但是当我们点击后退按钮时并不会回退到之前选中的演员图片。这里我们还需要在用户点击后退和前进按钮时使用另外一个Ajax请求来更新内容,并再一次使用pushState方法来更新页面的URL。

  我们使用pushState方法中的第一个参数(其中的state)来保存状态信息:

history.pushState(data, null, url);

  上面代码中的data参数在popstate事件触发时可以被获取到。当浏览器的后退和前进按钮被点击时会触发popstate事件。

window.addEventListener('popstate', function(e) {
// e.state表示上一个被点击的图片的data-attribute
});

  我们可以通过该参数传递一些我们需要的信息,例如在该示例中我们将之前选中的捉鬼敢死队的演员作为参数传递给requestContent方法,在该方法中,我们使用jQuery的load方法进行一次Ajax请求。

function requestContent(file) {
$('.content').load(file + ' .content');
} window.addEventListener('popstate', function(e) {
var character = e.state; if (character == null) {
removeCurrentClass();
textWrapper.innerHTML = " ";
content.innerHTML = " ";
document.title = defaultTitle;
} else {
updateText(character);
requestContent(character + ".html");
addCurrentClass(character);
document.title = "Ghostbuster | " + character;
}
});

  如果用户点击了演员Ray的图片,event listener会被触发,然后在pushState事件中保存图片的data属性的值。当用户点击另外一个图片,并点击了浏览器的后退按钮,此时popstate事件会被触发,从而重新加载ray.html页面。

  这意味着什么呢?当我们点击一个演员的图片然后将被更改的URL分享出去,用户访问这个URL时对应的HTML文件会被自动加载进来。这会带来一些更好的用户体验,并保证了URL和页面内容的一致性从而减少了因此而带给用户的一些困惑。

  上面的示例只是简单地通过jQuery来动态加载内容,我们当然也可以在pushState方法中传递一些更加复杂的对象。不过这个例子已经能足够说明问题并帮助我们开始学习如何使用History API的功能。我们先要学会走,然后才能跑。

下一步

  如果我们想大范围地使用这种技术,我们应该考虑使用一些专有的工具,例如pjax 它是一个jQuery的插件,使用它可以大大提高我们同时使用Ajax和pushState方法进行开发的速度,不过它只支持那些使用History API接口的现代浏览器。

  History JS可以兼容旧浏览器,对于不支持History API接口的浏览器,它依然使用旧的URL hash的方式来实现同样的功能。

有关URLs

  这里我特别引用了Kyle Neath有关URLs的说明:

URLs是一个通用的概念,它可以工作在Firefox, Chrome, Safari, Internet Explorer, curl, wget,甚至在你的iPhone, Android以及便签纸上。它是web中的一个通用的语法。不要认为这是理所当然的。任何一个稍微懂点技术的用户都可以浏览你的应用的90%以上的部分而不用去刻意记住那些URL的结构。要实现这样的效果,你需要考虑URLs的实用性。

  这意味着不论你想要进行什么样的hacks或性能优化,作为web开发人员,你应该注重URL。而随着HTML5 History API的帮助,我们可以轻松地解决诸如上述示例中的一些问题。

常见问题

  • 将Ajax请求的地址嵌入到a标记的href属性中通常是个不错的主意。
  • 确保在JavaScript的click事件处理程序中return true,这样当有人使用中键点击或命令点击时不会导致程序被意外覆盖。

补充

浏览器支持

Chrome Safari Firefox Opera IE Android iOS
31+ 7.1+ 34+ 11.50+ 10+ 4.3+ 7.1+

HTML5 History API让ajax能回退到上一页的更多相关文章

  1. HTML5 history API与ajax分页实例页面

    <ul id="choMenu" class="rel cho_menu"> <li><a href="ajax.php ...

  2. 一篇文章图文并茂地带你轻松实践 HTML5 history api

    HTML5 history api 前言 由于笔者在网络上没有找到比较好的关于 history api 的实践案例,有的案例过于杂乱,没有重点,有些案例只是告诉读者 api 是什么,却没告诉怎么用,本 ...

  3. html5 history api

    1.html5 history api适用场景,个人理解最大的用处是配合ajax使用,使ajax拥有回退.前进的用户体验. 2.代码(dive into html5中的一个小例子) 1)fer.htm ...

  4. 转: html5 history api详解~很好的文章

    从Ajax翻页的问题说起 请想象你正在看一个视频下面的评论,在翻到十几页的时候,你发现一个写得稍长,但非常有趣的评论.正当你想要停下滚轮细看的时候,手残按到了F5.然后,页面刷新了,评论又回到了第一页 ...

  5. window.history的跳转实质-HTML5 history API 解析

    在上一浏览器跳转行为的测试中,我们看到了通过不同的方法操作浏览器跳转时,它的刷新表现有所不同,在这一文章中,将看看,为何会产生这样的不同?其背后的实质是什么?浏览器的访问历史记录到底是如何运作的呢? ...

  6. HTML5 history API,创造更好的浏览体验

    HTML5 history API有什么用呢? 从Ajax翻页的问题说起 请想象你正在看一个视频下面的评论,在翻到十几页的时候,你发现一个写得稍长,但非常有趣的评论.正当你想要停下滚轮细看的时候,手残 ...

  7. 转:HTML5 History API 详解

    从Ajax翻页的问题说起 请想象你正在看一个视频下面的评论,在翻到十几页的时候,你发现一个写得稍长,但非常有趣的评论.正当你想要停下滚轮细看的时候,手残按到了F5.然后,页面刷新了,评论又回到了第一页 ...

  8. HTML5 History API实现无刷新跳转

    在HTML5中, 新增了通过JS在浏览器历史记录中添加项目的功能. 在不刷新页面的前提下显示改变浏览器地址栏中的URL. 添加了当用户单击浏览器的后退按钮时触发的事件. 通过以上三点,可以实现在不刷新 ...

  9. HTML5 history API实践

    一.history API知识点总结 在HTML4中,我们已经可以使用window.history对象来控制历史记录的跳转,可以使用的方法包括: history.forward();//在历史记录中前 ...

随机推荐

  1. SpringCloud stream连接RabbitMQ收发信息

    百度上查的大部分都是一些很简单的单消费者或者单生产者的例子,并且多是同一个服务器的配置,本文的例子为多服务器配置下的消费生产和消费者配置. 参考资料:https://docs.spring.io/sp ...

  2. excel合并同类项去重求和功能

    参考:百度经验 主要利用函数为:sumif(range,criteria,[sum_range]) Range:条件区域,用于条件判断的单元格区域. Criteria:求和条件,由数字.逻辑表达式等组 ...

  3. 在CLion项目中指定不同版本的链接库

    在项目中, 需要使用到libevent-2.1.x, 但是Ubuntu16.04自带的libevent版本为2.0.5, 需要另外编译安装新版的libevent, 安装过程很简单 -stable.ta ...

  4. Paint的基本使用

    代码地址如下:http://www.demodashi.com/demo/14712.html 前言 在讲述自定义控件的时候,我们讲到了自定义控件的基本步骤,那么在自定义控件中,我们第一个需要了解的就 ...

  5. 聊聊单元测试(三)——Spring Test+JUnit完美组合

    本着“不写单元测试的程序员不是好程序员”原则,我在坚持写着单元测试,不敢说所有的Java web应用都基于Spring,但至少一半以上都是基于Spring的. 发现通过Spring进行bean管理后, ...

  6. 时间选择器(js,css,html)

    忘了从哪下载的了, 整理电脑, 做个记录, 效果图: 下载链接: 链接: https://pan.baidu.com/s/1qfYkcfn8dtLFg0oniB2GHA 提取码: 2amm

  7. Ubuntu下看不见pthread_create(安装pthread线程库)

    使用下面的命令就可以了! sudo apt-get install glibc-doc sudo apt-get install manpages-posix-dev 然后在用man -k pthre ...

  8. Fedora下使用minicom及USB串口线

    一.minicom Fedora不像以前的RedHat,不能直接输入minicom回车,因为在目录/dev/下面没有modem这个子目录,而minicom的运行默认是有这个目录的,所以直接敲是不行的. ...

  9. ROS学习(七)—— 理解ROS Topic

    一.准备工作 1.打开roscore roscore 2.turtlesim 打开一个turtulesim节点 rosrun turtlesim turtlesim_node 3.turtle key ...

  10. HDU 4585 Shaolin (STL)

    Shaolin Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)Total Sub ...