几年前,Ajax的兴起给互联网带来了新的生机,同时也使用户体验有了质的飞跃,用户无需刷新页面即可获取新的数据,而页面也以一种更具有交互性的形式为用户展现视图,可以说这种变化对互联网发展的贡献是史无前例的。

但随着Ajax大规模应用,越来越多的开发人员开始注意到其中存在的问题,因为Ajax的视图展现是在页面无刷新情况下进行的,这也就意味着在用户做了一系列操作之后,页面的URL是没有任何变化的,这些操作所产生的结果自然也就无法保留,当用户再次访问时,想要展现的数据也就无法重现。

举个例如来说,我们在一个摄影网站浏览一个相册,点击第一张图片会动态弹出一个大图的相框,可以更好地浏览图片,当看到一个自己喜欢的图片时,急于分享这张图片,于是把当前的URL发布出去了,但是我们的好友点击进去后发现,看到的只是原来的那个相册,并没有弹出的大图相框,更别说我们要分享的那张图片了,是不是很失望。也正是因为如此,这个页面无法被搜索引擎精确地抓取,SEO无法做到优化,用户的可访问性也大打折扣。

为了解决这个问题,开发者开始尝试使用URL中的hash来提高可访问性,部分浏览器开始提供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类似,唯一不同的是,执行这个操作后,浏览器会对会话历史栈内的记录进行替换而不是添加,这在特定场景下是比较适用的。

我们这里就来结合实例来讲解一下如何操作历史记录。

正如上图看到的那样,页面初始化时展现三张小图,点击任意一张小图时,会弹出预览图,页面的基本框架如下:

<!DOCTYPE html>
<html><head><title>History</title><link rel="stylesheet" type="text/css" href="css/main.css"></head><body><ul id="gallery"><li><img src="img/1.jpg" data-id="1"/></li><li><img src="img/2.jpg" data-id="2"/></li><li><img src="img/3.jpg" data-id="3"/></li></ul><div id="shadow-layer" class="for-dialog"></div><div id="preview-panel" class="for-dialog"><img id="preview-img"/><div id="preview-info"></div><div id="preview-close" class="preview-button">Close</div><div id="preview-prev" class="preview-button"><</div><div id="preview-next" class="preview-button">></div></div></body><script type="text/javascript" src="js/jquery.js"></script><script type="text/javascript" src="js/main.js"></script>
</html>

然后页面初始化时,我们需要为一些DOM元素绑定事件,就像下面这样:

//点击小图开始预览模式
$('#gallery img').click(function() {preview(parseInt($(this).attr('data-id')));
});
//关闭预览视图
$('#preview-close').click(function() {$('.for-dialog').hide();window.history.back();
});
//点击预览上一张
$('#preview-prev').click(function() {switchPrev();
});
//点击预览下一张
$('#preview-next').click(function() {switchNext();
});

可以看到几个绑定的事件,点击小图会触发预览模式;然后在预览时点击关闭按钮,会隐藏预览框,同时调用了history的back函数,我们稍后会介绍;最后是两个切换按钮的事件,分别会切换到上一张或下一张大图。我们注意到,当小图被点击时,调用了一个叫preview的函数,这个函数正是我们重点要讲的,代码如下:

function preview(id, isSwitch) {$('.for-dialog').show();//获取对应的数据信息var data = getPreviewDataById(id);$('#preview-img').attr('src', data.img);$('#preview-info').html(data.info);//记录当前预览图片的ID,在前后切换时取用$('#preview-panel').attr('data-prview-id', id);var stateObj = {id: id};var operation = isSwitch ? 'replaceState' : 'pushState';//往历史记录栈中放入一条记录或者替换一条记录window.history[operation](stateObj, 'img: ' + id, '?preview=' + id);
}

上面的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替换当前历史栈中那条记录即可,假如我们点击右边的切换按钮,应该是这样的:

window.history.replaceState({id: 3}, 'img: 3', '?preview=3');

为了调用replaceState函数,我们需要在执行preview函数时,传入一个isSwitch参数,我们来看看是如何调用的:

//预览下一张
function switchNext() {//根据当前ID计算下一个IDvar currId = parseInt($('#preview-panel').attr('data-prview-id'));currId++;currId > 3 && (currId = 1);preview(currId, true);
}

下面就以两个示意图来讲解一下切换前后的栈结构:

这样我们就能保证历史栈中只有一条记录,当关闭预览框结束预览时,可立即恢复到之前的视图。

而向前切换也是一样的,代码如下:

//预览上一张
function switchPrev() {//根据当前ID计算前一个IDvar currId = parseInt($('#preview-panel').attr('data-prview-id'));currId--;currId < 1 && (currId = 3);preview(currId, true);
}

到这里,我想大家都已经清楚如何操作历史栈来改变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事件是如何被触发的:

window.onpopstate = function(e) {console.log('e.state:', e.state);
};

在这个事件函数内,我们会打印事件对象中的state对象,其实就是我们执行pushState函数时放进去的对象,下面我们在控制台做下面几个操作看看效果:

如图所示,我们放入两条记录,当后退或前进的时候,都会执行到onpopstate事件函数,并且能够获取到当前的状态对象信息,如果我们的图片预览功能需要记录每一步的操作,那么我们就可以在onpopstate事件函数中获取到当前需要展现的图片ID,然后直接调用preview函数即可。

最后,需要注意的是,当前有些浏览器还不能很好的支持History API,所以我们最好判断一下浏览器的支持情况:

var isHistoryStateSupported = 'pushState' in window.history;

讲到这里,关于History的相关知识及应用基本上就告一段落了,希望大家能够细细体会它的应用场景,并加以练习,最后希望大家能够很好地掌握并运用到未来的项目中去。

HTML5新特性之History相关推荐

  1. html5新特性:利用history的pushState等方法来解决使用ajax导致页面后退和前进的问题

    html5新特性:利用history的pushState等方法来解决使用ajax导致页面后退和前进的问题 参考文章: (1)html5新特性:利用history的pushState等方法来解决使用aj ...

  2. Html5新特性归纳

    Html5新特性 文章目录 Html5新特性 一.简介 二.十大新特性 1.语义化结构化标签 1.section 2.header 3.footer 4.nav 5.article 6.aside 7 ...

  3. HTML5新特性(新元素、更强大的forms、整合的API)

    文章目录 HTML5新特性 新元素 Forms变化 Integrated API HTML5新特性 编写html5文档时,你最先注意到的新特性是文档类型声明: <!DOCTYPE HTML> ...

  4. 关于html5支持与否的判断(JS检测是否支持HTML5新特性)

    未来使用H5的场景会越来越多,这是令 web开发者欢欣鼓舞的事情.然而有一个现实我们不得不看清,那就是IE系列浏览器还占有一大部分市场份额,以IE8.9为主,windows8.1的用户已经用上了IE1 ...

  5. 百度开发者大会-《用HTML5新特性开发移动App》PPT分享

    今天百度开发者大会,移动互联网分论坛,我的主题演讲<用HTML5新特性开发移动App>PPT分享如下. 完整PPT可在Slideshare观看,或者在百度开放云平台上下载到.

  6. HTML中三维特性,前端进阶系列(三):HTML5新特性

    HTML5 是对 HTML 标准的第五次修订.其主要的目标是将互联网语义化,以便更好地被人类和机器阅读,并同时提供更好地支持各种媒体的嵌入.HTML5 的语法是向后兼容的.现在国内普遍说的 H5 是包 ...

  7. 【阿里云大学课程】前端必知——HTML5新特性完整视频教程(音频、视频、画布、web存储、动画……)...

    HTML5是HTML最新的修订版本,2014年10月由万维网联盟(W3C)完成标准制定,其设计目的是为了在移动设备上支持多媒体. 本课程中,你将学习到下列这些HTML5新特性(点击下列课时立即学习): ...

  8. html5 logo svg,HTML5新特性之用SVG绘制微信logo

    html5新特新html5 中的一些有趣的新特性:1.用于绘画的 canvas 元素 2.用于媒介回放的 video 和 audio 元素 3.对本地离线存储的更好的支持 4.新的特殊内容元素,比如 ...

  9. html5储存类型特点,避免踩雷!你不得不知的 HTML5 “新”特性

    什么是 HTML5 HTML的发展历程如下: 产生于1990年 1997年 HTML4 出现,成为互联网开发的标准 2008年,HTML5正式出现,2002年趋于稳定 HTML在发展过程中,HTML4 ...

最新文章

  1. centos 找不到php.ini,centos找不到php.ini文件
  2. 管道 过滤器风格 java_完成基于管道过滤器风格的KWI实现.doc
  3. 安装多个java后,java版本不对
  4. 面向对象与基于对象 区别
  5. Sharepoint学习笔记---如何在Sharepoint2010网站中整合Crystal Report水晶报表(显示数据 二)...
  6. Maven中Spring-Data-Redis存储对象(redisTemplate)
  7. 【VSCode快捷键大合集】
  8. C# 导入CSV文件,导出到CSV文件
  9. 数据结构专题(一):1.2.求元素个数,取元素与定位
  10. 浏览器内核选型列表,请大家继续补充
  11. oracle10g   RMAN增量备份策略
  12. 雄迈H.265 DVR程序功能升级简介
  13. 苹果电子邮件怎么注册_电子邮件地址怎么写
  14. python颜色对照表及颜色搭配
  15. iOS使用颜色生成图片的暗黑适配
  16. RTL8367RB -CG
  17. WEEX UI 官网
  18. docker 常用命令 -----(批量查看镜像/容器,重命名,运行,进入容器,私有仓库)
  19. php ThinkPHP文章上一篇、下一篇解决方案
  20. tsf定时任务迁移到xxl-job

热门文章

  1. CT图像密度分辨力和空间分辨力的区别和联系
  2. TypeScript error in node_modules/jest-diff/build/diffLines.d.ts
  3. Python(jupyter notebook)--K-means聚类实例
  4. Euclidean division
  5. alta公司1553b板卡编程相关
  6. 信创操作系统--统信UOS桌面版(登录与激活统信:直接登录、远程登录、锁屏、电源管理、激活)
  7. ArcGIS基础:清除数据坐标系信息的操作
  8. sizzle.js学习笔记利用闭包模拟实现数据结构:字典(Map)
  9. iOS-使用Masonry布局不能立即获取到frame
  10. PAT甲级Invert a Binary Tree 柳神层序遍历的思路值得借鉴