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

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

基本知识

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

  注意其中的pushState和replaceState这两个方法。我们可以在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+

原文地址:https://css-tricks.com/using-the-html5-history-api/

使用HTML5的History API的更多相关文章

  1. 锚接口(下)——html5的history api

    概述 虽然html5的history api是H5专门用来解决记录历史记录和单页面的方法,但是很多老式的浏览器并不支持它,所以一般遇到老式的浏览器会做一个polyfill使用之前的hashchange ...

  2. 利用HTML5的History API实现无刷新跳转页面初探

    HTML4中的History API history这个东西大家应该都不陌生,我们经常使用history.back(-1)来实现后退功能,具体的属性和方法如下: 属性 length 历史的项数.Jav ...

  3. 有关html5的history api

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

  4. Html5 history Api简介

    一. Html4的History API back() 后退,跟按下“后退”键是等效的. forward() 前进,跟按下“前进”键是等效的. go() 用法:history.go(x):在历史的范围 ...

  5. HTML5学习笔记之History API

    这系列文章主要是学习Html5相关的知识点,以学习API知识点为入口,由浅入深的引入实例,让大家一步一步的体会"h5"能够做什么,以及在实际项目中如何去合理的运用达到使用自如,完美 ...

  6. 大熊君学习html5系列之------History API(SPA单页应用的必备------重构完结版)

    一,开篇分析 Hi,大家好!大熊君又和大家见面了,(*^__^*) 嘻嘻……,这系列文章主要是学习Html5相关的知识点,以学习API知识点为入口,由浅入深的引入实例, 让大家一步一步的体会" ...

  7. 大熊君学习html5系列之------History API(SPA单页应用的必备)

    一,开篇分析 Hi,大家好!大熊君又和大家见面了,(*^__^*) 嘻嘻……,这系列文章主要是学习Html5相关的知识点,以学习API知识点为入口,由浅入深的引入实例, 让大家一步一步的体会" ...

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

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

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

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

随机推荐

  1. 【web maven】新建的项目 controller也有,从前台跳转后台 无法找到对应的controller

    碰上很 愣的问题: 使用maven搭建项目完成,项目页面写好,实体.Dao.Service.Controller都有了,但是指定Controller中的某个方法中一直不能从前台进入后台 原因: 没有w ...

  2. WPF CodeBehind后台动态创建图片及添加事件

    问题:WPF中DataGrid需要动态生成列并绑定值,首列包含图片和文本,点击图片触发事件. 难点:1.图片资源在VisualTree中的绑定   2.图片的事件绑定 public class Mai ...

  3. 洛谷 P1827 美国血统 American Heritage Label:字符串Water

    题目描述 农夫约翰非常认真地对待他的奶牛们的血统.然而他不是一个真正优秀的记帐员.他把他的奶牛 们的家谱作成二叉树,并且把二叉树以更线性的“树的中序遍历”和“树的前序遍历”的符号加以记录而 不是用图形 ...

  4. 【java基础学习】-【泛型】

    参考以下几位同学的总结来学习: http://www.cnblogs.com/lwbqqyumidi/p/3837629.html#!comments http://www.weixueyuan.ne ...

  5. 原生js封装二级城市下拉列表

    闲的蛋疼,封装了个二级城市下拉 先保证html里有 <select id="province" size=1 > </select> <select ...

  6. 关于C#调用非托管动态库方式的性能疑问

    最近的项目中,因为一些原因,需要C#调用非托管(这里为C++)的动态库.网上喜闻乐见的方式是采用静态(DllImport)方式进行调用.偶然在园子里看到可以用动态(LoadLibrary,GetPro ...

  7. 学习angular2

    最近在学angular2,并没有很深入,跟着官网做了一份练习,整理了一下,画了一个简单的结构图.

  8. C#判断数组是否为空

    string[] array=new[] { "1", "2", "3", "4", "5" }; ...

  9. linux终端 字符界面 显示乱码

    方法一:配置SSH工具 SecureCRT中文版配置 [全局选项]→[默认会话]→[编辑默认设置]→[终端]→[外观]→[字体]→[新宋体 10pt CHINESE_GB2312]→[字符编码 UTF ...

  10. <十>JDBC_处理Blob类型数据

    /*  * 读取BLOB数据:  *  使用getBlob方法读取到Blob对象  *  调用Blob的getBinaryStream(方法得到输入流,在使用IO操作  * */ @Test publ ...