HTML5新特性之History
几年前,Ajax的兴起给互联网带来了新的生机,同时也使用户体验有了质的飞跃,用户无需刷新页面即可获取新的数据,而页面也以一种更具有交互性的形式为用户展现视图,可以说这种变化对互联网发展的贡献是史无前例的。
但随着Ajax大规模应用,越来越多的开发人员开始注意到其中存在的问题,因为Ajax的视图展现是在页面无刷新情况下进行的,这也就意味着在用户做了一系列操作之后,页面的URL是没有任何变化的,这些操作所产生的结果自然也就无法保留,当用户再次访问时,想要展现的数据也就无法重现。
举个例如来说,我们在一个摄影网站浏览一个相册,点击第一张图片会动态弹出一个大图的相框,可以更好地浏览图片,当看到一个自己喜欢的图片时,急于分享这张图片,于是把当前的URL发布出去了,但是我们的好友点击进去后发现,看到的只是原来的那个相册,并没有弹出的大图相框,更别说我们要分享的那张图片了,是不是很失望。也正是因为如此,这个页面无法被搜索引擎精确地抓取,SEO无法做到优化,用户的可访问性也大打折扣。
为了解决这个问题,开发者开始尝试使用URL中的hash来提高可访问性(hash 属性是一个可读可写的字符串,该字符串是 URL 的锚部分(从 # 号开始的部分)),部分浏览器开始提供hash相关的事件支持,有些知名站点也和搜索引擎约定使用某种规则来对站点页面进行抓取,但这毕竟不是一个标准的技术,还是不可避免的存在很多问题,开发者也期待出现一个标准化的完美解决方案,彻底解决这个难题。
幸运的是,HTML5中的History API给开发者带了新的希望,它很好的支持了基于URL的页面无刷新操作,也使得SEO优化得到完美的解决,毫无疑问,History API将会成为Web领域未来的标准,越来越多的开发者也将会使用它来开发自己的应用程序。
那么今天,我们就来讲解一下与History相关的技术。
为了能够更好的阐述这项新技术,我们选择使用一个示例程序来开始。如下图所示,我们展示三张小图,点击任意一张,将会弹出大的预览图,然后可以前后切换预览图片:
这个程序对于大家来说应该说是比较简单的,当点击小图时,将放置大图的页面元素显示出来,图片和简介显示出与小图对应的信息即可,切换预览图片的操作也是非常容易做到的。
这就是传统的Ajax操作,而这种操作存在一个极其严重的缺陷,我们无法记录当前浏览的图片信息,假如后面的小图有几百张,当我们浏览到某一张时,觉得非常不错,想把它发送给朋友,也有可能想自己存下来,这时候我们会发现,URL中没有任何这张图片的信息,复制地址栏也就没有任何效果了。
所以我们极为迫切的需要在URL中加入用户操作所产生的信息,这个时候History API就派上用场了,他提供的方法可以在页面不刷新的情况下对URL进行更新操作。下面就介绍一下主要的两个方法:
history.pushState(stateObj, title, url);
这个方法会往当前会话的历史栈中放入一条记录,stateObj是用户自定义的对象,用于记录一些有用的数据,title顾名思义就是标题信息,第三个参数是要放入的url信息。例如我们点击第二张图片时可以执行history.pushState({id: 2}, 'img: 2', '?preview=2');
history.replaceState(stateObj, title, url);
这个方法于pushState类似,唯一不同的是,执行这个操作后,浏览器会对会话历史栈内的记录进行替换而不是添加,这在特定场景下是比较适用的。
我们这里就来结合实例来讲解一下如何操作历史记录。
正如上图看到的那样,页面初始化时展现三张小图,点击任意一张小图时,会弹出预览图,页面的基本框架如下:
然后页面初始化时,我们需要为一些DOM元素绑定事件,就像下面这样:
可以看到几个绑定的事件,点击小图会触发预览模式;然后在预览时点击关闭按钮,会隐藏预览框,同时调用了history的back函数,我们稍后会介绍;最后是两个切换按钮的事件,分别会切换到上一张或下一张大图。我们注意到,当小图被点击时,调用了一个叫preview的函数,这个函数正是我们重点要讲的,代码如下:
上面的preview函数主要负责几个重要的工作,首先显示预览框,然后根据数据ID获取数据信息,最后操作历史记录对象。在实际开发中,getPreviewDataById函数大多是以Ajax方式进行的,这个时候只需将后面的逻辑放到回调函数内部即可。在preview函数的最后,我们往历史记录里面放入或替换一条包含ID信息的对象,同时改变URL,放入还是替换,取决于是不是前后切换预览图,我们稍后会详细解释一下。注意,这个时候虽然URL改变了,但页面并不会刷新。下面我们就来对比一下点击前后的效果,以点击第二张小图为例:
可以明显看到,地址栏已经变化了,这其实就是我们调用pushState函数的结果:
window.history.pushState({id: 2}, 'img: 2', '?preview=2');
需要额外注意的一点是,第三个参数是新的URL地址,根据官方文档介绍,它可以是相对地址,也可以是绝对地址,所以我们也可以选择使用绝对地址,就像下面这样,效果是一样的:
window.history.pushState({id: 2}, 'img: 2','http://localhost/history/?preview=2');
在上面的preview函数参数列表中,存在一个isSwitch参数,如果函数被调用时指定这个参数,则会调用replaceState,而不是pushState,这是为什么呢,下面就来解释一下。
还记得我们我们为预览框右上角关闭按钮绑定的事件吗,除了隐藏预览图,还调用了history的back函数,其实我们是要利用这个函数从历史栈中弹出之前放入的历史记录,进而将URL恢复到预览前的状态。那么,如果我们在预览的时候频繁的切换到下一张或者上一张,历史记录栈中就会存在很多记录,要回到初始状态显然不易,我们也没有必要保留这么多的历史记录,所以当用户前后切换预览图时,只需要调用replaceState替换当前历史栈中那条记录即可,假如我们点击右边的切换按钮,应该是这样的:
为了调用replaceState函数,我们需要在执行preview函数时,传入一个isSwitch参数,我们来看看是如何调用的:
下面就以两个示意图来讲解一下切换前后的栈结构:
这样我们就能保证历史栈中只有一条记录,当关闭预览框结束预览时,可立即恢复到之前的视图。
而向前切换也是一样的,代码如下:
到这里,我想大家都已经清楚如何操作历史栈来改变URL,那么我们不仅要问,如果使用当前的URL重新访问站点,如何还原关闭之前的视图呢,其实这个就属于基本的操作了,只需要在onload函数时得到相应的参数,然后调用preview函数即可,如代码所示:
//从URL中获取到当前预览图片的ID
function getCurrPreviewId() {
var search = location.search;
if (!search) return -1; var params = {}; var keyValues = search.substring(1).split('&');
keyValues.forEach(function(item) {
var parts = item.split('=');
params[parts[0]] = parts[1];
});
if (!params['preview']) return -1; return params['preview'];
} //当页面加载时,如果URL中有预览信息,取出然后
window.onload = function() {
var id = getCurrPreviewId();
if (id < 1 || id > 3) return; preview(id);
};
到这里其实我们已经将整个过程讲解完成了,但还有一个重要的事件我们还没有涉及到,那就是window下面的onpopstate事件,当用户点击后退按钮或前进按钮时,或者当我们的程序执行history.back(),history.forward(),history.go()时,都会触发这个事件,它能够捕获这些操作完成之后,当前历史栈中的状态,我们可以利用这些信息,做进一步的操作。那么我们就来做个试验,看看onpopstate事件是如何被触发的:
在这个事件函数内,我们会打印事件对象中的state对象,其实就是我们执行pushState函数时放进去的对象,下面我们在控制台做下面几个操作看看效果:
如图所示,我们放入两条记录,当后退或前进的时候,都会执行到onpopstate事件函数,并且能够获取到当前的状态对象信息,如果我们的图片预览功能需要记录每一步的操作,那么我们就可以在onpopstate事件函数中获取到当前需要展现的图片ID,然后直接调用preview函数即可。
最后,需要注意的是,当前有些浏览器还不能很好的支持History API,所以我们最好判断一下浏览器的支持情况:
讲到这里,关于History的相关知识及应用基本上就告一段落了,希望大家能够细细体会它的应用场景,并加以练习,最后希望大家能够很好地掌握并运用到未来的项目中去。
HTML5新特性之History的更多相关文章
- html5新特性
这一篇博文不会告诉你怎么去使用html5的新特性,只会给你总结一下新特性------对于好学的人可以把这篇文章当做一个目录 对于初接触的人来说是一个导向 对于已经接触过的人来说是一个检测你掌握程度的检 ...
- 转: HTML5新特性之Mutation Observer
转: HTML5新特性之Mutation Observer Mutation Observer是什么 Mutation Observer(变动观察器)是监视DOM变动的接口.当DOM对象树发生任何变动 ...
- HTML5新特性之CSS+HTML5实例
1.新的DOCTYPE和字符集 HTML5的一项准则就是化繁为简,Web页面的DOCTYPE被极大的简化. <!DOCTYPE html> 同时字符集声明也被简化了: <meta c ...
- HTML5新特性:FileReader 和 FormData
连接在这里: HTML5新特性:FileReader 和 FormData
- web全栈架构师[笔记] — 03 html5新特性
HTML5新特性 一.geolocation PC端 精度比较低 通过IP库定位 移动端 通过GPS window.navigator.geolocation 单次 getCurrentPositio ...
- HTML5新特性:范围样式
原文出处:http://blog.csdn.net/hfahe/article/details/7381141 Chromium 最近实现了一个HTML5的新特性:范围样式,又叫做< ...
- html5新特性与用法大全了解一下
有好多小伙伴私聊我问我html5新特性 和用法,下面我给大家具体介绍一下html5都新加了哪些新特性,下面我给大家总结一下. 1)新的语义标签 footer header 等等2)增强型表单 表单2. ...
- html5新特性contenteditable 属性更容易实现动态表单
介绍html5新特性的一个属性:contenteditable 作用域全局.所有的块标签都可以,例如:span.p.div.td等标签.但是,不可以作用域<br/>类型的标签. conte ...
- HTML5新特性之文件和二进制数据的操作 Blob对象
HTML5新特性之文件和二进制数据的操作 1.Blob对象 2.FileList对象 3.File对象 4.FileReader 对象 5.URL对象
随机推荐
- 【旧文章搬运】超级无敌大炉子的LzOpenProcess
原文发表于百度空间,2008-11-20========================================================================== 这个东西嘛 ...
- VC链接hid.lib出错问题解决方案
写一个HID的客户端小程序,调用了一些HID API,但是链接时出现了奇怪的现象. 尽管我已经把头文件和lib文件加入到了VC的Include和Lib目录中,但不管我用VC链接,还是在DDK环境下链接 ...
- jsp 验证码
<%@page import="java.awt.Graphics2D"%> <%@page import="java.util.Random" ...
- (二十四)权限控制(粗粒度)-通过filter
扩展:权限控制(粗粒度)-通过filter 过滤器编写步骤: 1.编写一个类 实现filter接口 重写方法 2.编写配置文件 <filter> <filter-mapping> ...
- git status -s 的表达式的读法
1 2 读法 解决方案 ?? 未被追踪 git add -A 或git add 文件路径 A 新添加文件 注:??被add后的状态 OK,等待commit. M 已 ...
- POJ2576【背包】
题意: 每个人必须在一个团队或其他; 人对两支球队的数量不得超过1不同; 人们对各队的总重量应尽可能接近相等越好. 思路: 那么我求一个能接近最接近总和一半的值. 每个人的值就是物品,每个物品有且只有 ...
- 关于国债的一些计算: 理论TF价格1(缴款日前无付息)
计算 ExpectedTFPrice 是一个比较复杂的计算,我们这里讨论简单的一种情况. 给定一只可交割国债bond(一般为CTD),一个国债期货tf,一个日期t(表示tf的一个交易日期,我们通过t日 ...
- python __builtins__ type类 (69)
69.'type', 返回对象类型 class type(object) | type(object_or_name, bases, dict) | type(object) -> the ob ...
- 洛谷P4725 【模板】多项式对数函数(多项式运算)
传送门 前置芝士:微积分(有所了解即可)(可以看看这篇,写得非常详细我看了两章就看不下去了) 以下都是一些简单的教程切莫当真,仅供理解,建议看更严谨的 导数:对于一个函数$f(x)$,它的导数$f'( ...
- 工作中常用css样式总结
一.HTML隐藏文本输入框 有三种方法: 1.<input type="hidden" value=""> 这是对任何元素都起作用的: 2.< ...