html5 history.pushState()方法实现无刷新更新地址
最近遇到了在不刷新页面的情况下修改浏览器url链接的需求,遂求助于万能的度娘,最终通过history.pushState()完美解决问题。现在将我理解的一些内容分享一下,不对的地方欢迎大家指出。
在使用方法前首先需要了解它的兼容性,history.pushState()存在一定的兼容性问题,下图展示了部分浏览器的兼容情况,当然也可以通过 https://caniuse.com/#search=pushState n 查看支持的浏览器版本。
简单介绍一下方法的相关信息:
history.pushState(stateObject, title, url);
history.pushState()主要是在不刷新浏览器的情况下,创建新的浏览记录并插入浏览记录队列中。
1.状态对象(stateObject)--stateObject是一个JavaScript对象,通过pushState方法可以将stateObject内容传递到新页面中。
2.标题(title)--几乎没有浏览器支持该参数,但是传一个空字符串会比较安全。
3.地址(url)--新的历史记录条目的地址(可选,不指定的话则为文档当前URL);浏览器在调用pushState()方法后不会加载该地址;传入的URL与当前URL应该是同源的,否则,pushState()会抛出异常。
下面以期权记为例具体说明:
打开博客园官网链接 https://www.qiquanji.com/mip/,F12打开控制台并输入 window.history,可以得到window.history的相关信息,如下图所示,我们关注的主要是红框标出的三个信息,其中length为浏览记录队列长度,由于这个页面是新打开的,队列中只有当前页面链接相关信息,其值为1,state值将在下面介绍,pushState即history.pushState(),不多赘述。
history.replaceState 和 history.pushState的使用和注意事项
HTML5引入了history.pushState()和history.replaceState()这两个方法,他们允许添加和修改history实体。同时,这些方法会和window.onpostate事件一起工作.
replaceState是替换当前的历史信息,pushState是添加一条新的历史记录
这两个方法有三个参数(state,title,url);
第一个参数是一个json格式的参数,他可以存储我们在当前历史以后会用到的数据,也可以传null;第二个官方的介绍是说页面的标题,不过暂时用不到,直接传null就可以了;第三个参数是当前历史的url,如果为null则表示当前历史的url和上一历史的url没发生变化
示例:
history.pushState({name:”名字”},null,”?name=张三”);
注意:
1、使用这两个方法时并不会直接触发onpopstate,而是存储了历史记录后前进或后退才会会触发onpopstate事件
2、history.pushstate有安全限制,其中之一是不允许应用推动跨url-s历史起源;服务器ip的端口不同和直接在电脑本地运行的html都会报错:
一、history对象分析
浏览器是通过 window对象的 history对象来对浏览器历史访问记录,从而可以实现前进和后退。history对象可以理解其保存了一个有序的列表对象,每个对象都代表了一个页面信息(包括页面的url等信息),注意当前页面也被保存在里面。
这样就可以通过浏览器本身提供的前进和后退按钮来操作,也可以利用javascript调用history对象的back(),forward(),和go()方法来实现页面的切换。
我们先来理解下history的机制。history对象中记录了浏览器窗口访问过的url,但出于安全考虑,无法通过程序获取history对象中的具体信息,只能通过back、forward、go方法进行页面跳转,此外length属性记录了history中的记录(url)条数。
我们设想下,当在浏览器窗口打开第一个地址,比如 url1时, 这时history中就有了url1这个记录,且length属性值为1,history对象中有个当前页面指针(从概念上可以这么理解)指向url1;如果再打开一个url2页面(无论是通过在地址栏直接输入、或通过url1中的链接或js代码打开),这时history中就有了url1和url2这两个记录,是一个有序的列表,这时length属性值为2,history对象中的当前页面指针指向url2,这时url2是最新的页面,页面不可以前进,但可以后退到url1,这时如果点击浏览器本身提供的后退按钮(或用js调用back方法),这时url1页面会被重新加载显示,history对象的length仍然为2,url1和url2组成的列表仍然不变,但history对象中的当前页面指针指向url1了,这时就不能后退但可以前进了。可以理解成一个数据结构中的双向链表机制。
通过上面的描述我们可以看出,我们说的历史记录都是指一个完整的页面请求url,而ajax并不是一个完整的页面请求,因此浏览器无法记录ajax的操作信息。
二、history对象的新特性
HTML5引入了histtory.pushState()和history.replaceState()这两个方法,它们会更新history对象的内容。同时,结合window.onpostate事件,就可以解决文章开头提出的问题。
我们先来看pushState方法的含义,我们通过举例子的方式来更好的说明,先给出一段代码:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <script type="text/javascript" src="../js/jquery-1.11.3.min.js"></script> </head> <body> <button onclick="doPushState()">pushState 按钮01</button> <button onclick="count()">count 按钮02</button> <script> var index=1; $(function(){ alert(location.href); }); function doPushState(){ history.pushState({}, "newtitle","test"+(index++)+".html"); } function count(){ alert(window.history.length); } </script> </body> </html>
上面是一个完整的html文件,文件名为demo.html。 把该文件放到web服务器上,从浏览器访问。如果是直接从本地磁盘打开,文件中的js代码执行会报错误。
在一个新的浏览器窗口访问该demo.html。 首先会执行 $()方法,弹出代码中的location.href信息。 这时执行count按钮,显示为1,注意如果在ie或chrome的新的浏览器窗口打开,值可能为2,因为它们的窗口会加载系统默认的一个页面,不是一个空白的窗口。
这时我们每点击一下pushState按钮,发现浏览器的地址会发生变化,先后变为test1.html , test2.html, test3.html, .......,并且通过点击count按钮发现,弹出的值加1. 这说明每调用一次pushState方法,history中就会新增加一条url记录。
我们先来解释下pushState方法,该方法有三个参数:
1)第一个参数是个js对象,可以放任何的内容,可以在onpostate事件中(后面介绍)获取到便于做相应的处理。
2)第二个参数是个字符串,目前好像没有起作用,可以传个空串。
3)第三个参数是个字符串,就是保存到history中的url。
结合例子的代码和输出可以看出,调用pushState方法的作用,就是相当于打开一个新页面,把当前页面作为历史记录,而当前的地址栏显示的是pushState方法中的url(这里是test.html)。但是与普通的打开一个新页面不同。浏览器将不会在调用pushState()方法后加载这个url,也就是说即使你写一个错误的url,也不会报错。
可以这么理解,当我们在一个新的浏览器窗口打开 demo.html后,点击n次pushState按钮后,history对象中存在这样的一个ulr列表。
demo.html(第1个url)----> index1.html(第2个url).......->index?.html(第n个url)----->indexn.html(当前页面的url)。
这时我们需要点击浏览器上的回退按钮n次,才能将浏览器上的地址退回到 demo.html。而且无论是在点击pushState按钮 或点击回退按钮的过程中发现,$()方法根本没有被触发,也就是说整个过程浏览器的页面内容都没有发生变化,变化的只是地址信息。
这也进一步说明,pushState只是将当前页面保存到history的历史记录中(并作为最近的一个记录),并且将当前浏览器的地址栏改为参数url的指定的值,但并不会加载它。这点与普通的通过链接打开或浏览器地址输入url完全不一样。
到了这里我们可以想象一下文章开头提出的问题了,如果我们在页面中执行一个ajax操作,当操作成功(如更新页面的局部内容)后,我们通过代码调用pushState方法,设置一个新的url,这样看上去就像发起了一个全新的请求,实际上只是个ajax操作。这时回退按钮也能用了,问题仅仅这样,回退按钮点了也没有任何反应。如果我们能通过代码,来响应这个回退按钮触发的事件,在事件中让界面恢复到ajax请求之前的界面,问题不就解决了吗?
得确如此,解决思路就是上面说的。下面我们来通过一个实际的例子看如何实现。在介绍例子之前,我们先来解释下html5中 history新增的另一个方法replaceState方法。
replaceState方法与pushState类似,同样有三个参数。区别在于,replaceState()是用来修改history对象中记录的当前页面的信息,它不是新建一个记录。如果将上面例子中的 代码 history.pushState({}, "newtitle","test"+(index++)+".html"); 中的pushState改为replaceState,其它代码都不动。这时我们点击pushState按钮后,看到的现象是一样的,地址栏的地址不断变化,页面内容不变。但我们点击count按钮,发现history中的记录数不变。这说明replaceState只是改变当前页面在history对象中的记录信息;而pushState是会产生一个新记录作为当前记录,把当前页面作为历史的记录保存。
我们再来看下window对象的popstate事件,当进行页面的前进或回退时,会触发该事件,并且在事件响应函数中通过 history.state 可以获取到 pushState方法和replaceState方法中第一个参数指定的对象。
解释了这几个api后,我们来一个具体的例子。
四、具体案例
我们来设想这样一个应用。一个页面来显示一篇长文章,该文章内容很长,分为很多章节。我们希望页面不会一次把所有章节的内容都加载起来,而是有一个章节导航,点击每个章节链接,通过jax加载具体章节的内容,而其它页面部分不需要要变化。
我们先看下传统的实现代码(注意,这里只注意核心逻辑代码的实现,其它的页面布局等尽量简化):
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <style type="text/css"> div { padding-bottom:100px; } </style> <script type="text/javascript" src="jquery-1.11.3.min.js"></script> </head> <body> <div style="float:left;border:1px solid red;margin:20px"> <p><a href="javascript:;" id="section1">第1章</a></p> <p><a href="javascript:;" id="section2">第2章</a></p> <p><a href="javascript:;" id="section3">第3章</a></p> </div> <div style="float:left;border:1px solid red;margin:20px" id="content"> </div> <script> $(function(){ //添加链接的处理事件 $("a").click(ajax); //加载默认的章节,默认显示第1章 $("#section1").trigger("click"); }); function ajax(event){ //实际的流程是发起ajax请求,获取内容并显示。这里为了简化,没有写实际的ajax请求。 //这段代码应该在ajax的请求响应中编写。 $("#content").html(this.id+"的内容"); var title = this.id; document.title = title; } </script> </body> </html>
本站声明:网站内容来源于网络,如有侵权,请联系我们https://www.qiquanji.com,我们将及时处理。
微信扫码关注
更新实时通知