Ajax保留浏览器历史的两种解决方案(Hash&Pjax)

总是在github down点东西,github整个界面做的不错,体验也很好~对于其中的源代码滑动的特效最为喜欢了~刚开始以为这个只是普通的ajax请求效果,但是发现这个特效能够导致浏览器地址栏跟随变化,并且再点击前进后退按钮后又可以将代码滑回滑出~~于是乎就来研究下吧~

一、通过锚点Hash实现:

在这方面其实国内很早就有做了,比如淘宝画报,通过的是在地址栏后面加#锚点实现的,浏览器是可以识别锚点为单位的历史记录的。但不是说页面本身有这个锚点,锚点的Hash只是起到一个引导浏览器将这次的记录推入历史记录栈顶的作用。

来做一个小小的demo:

Html代码 复制代码 收藏代码
  1. <style type="text/css"> 
  2.     #tab1_header,#tab2_header{ 
  3.         cursor:pointer; 
  4.         border:1px solid; 
  5.         width:50px; 
  6.     } 
  7.     #tab1,#tab2{ 
  8.         width:90%; 
  9.         height:200px; 
  10.         border:1px solid; 
  11.     } 
  12. </style> 
  13. <div id="tab_header"> 
  14.     <span id="tab1_header">Tab1</span> 
  15.     <span id="tab2_header">Tab2</span> 
  16. </div> 
  17. <div id="tab1">1</div> 
  18. <div id="tab2">2</div> 
    <style type="text/css">
        #tab1_header,#tab2_header{
            cursor:pointer;
            border:1px solid;
            width:50px;
        }
        #tab1,#tab2{
            width:90%;
            height:200px;
            border:1px solid;
        }
    </style>
    <div id="tab_header">
    	<span id="tab1_header">Tab1</span>
    	<span id="tab2_header">Tab2</span>
    </div>
    <div id="tab1">1</div>
    <div id="tab2">2</div>

一个很简单的Tab切换如果一般情况下就直接:

Js代码 复制代码 收藏代码
  1. $("#tab1_header").click(function() { 
  2.             $("#tab2").hide(); 
  3.             $("#tab1").show(); 
  4. }); 
  5. $("#tab2_header").click(function() { 
  6.             $("#tab1").hide(); 
  7.             $("#tab2").show(); 
  8. }); 

但假如点击到tab2时想通过后退按钮退到tab1时就不行了,假如刷新的话浏览器的行为完全不是出于用户的想法,这样的话,我们可以加入#锚点来模拟新页面,为什么要说模拟呢,假如直接通过js改变window.location浏览器会重新加载页面,但加#就不会重新加载并且能保存在历史中。JS通过window.location.hash来控制URL后面的锚点#。

我们把代码改为这样:

Js代码 复制代码 收藏代码
  1. $(function(){ 
  2.             showTab(); 
  3.             $(window).bind('hashchange', function(e){ 
  4.                 showTab(); 
  5.             }); 
  6.             $("#tab1_header").click(showTab1); 
  7.             $("#tab2_header").click(showTab2); 
  8.         }); 
  9.  
  10.         function showTab() { 
  11.             if (window.location.hash == "#tab2"){ 
  12.                 showTab2(); 
  13.             } else
  14.                 showTab1(); 
  15.             } 
  16.         } 
  17.         function showTab1() { 
  18.             $("#tab2").hide(); 
  19.             $("#tab1").show(); 
  20.             window.location.hash = "#tab1"
  21.         }; 
  22.         function showTab2() { 
  23.             $("#tab1").hide(); 
  24.             $("#tab2").show(); 
  25.             window.location.hash = "#tab2"
  26.         }; 

加上window.location.hash = "#tab1"这一段代码就行了,在点击tab后,地址栏后面就会加上#tab1,点击tab2后就会改成#tab2,当浏览器检测到url变化时就会触发hashchange这一事件,就是用户在点击后退时能够得到的事件就能够通过window.location.hash进行判断并进行ajax操作了,但是haschange这个事件并不是每个浏览器都有的,只有现代高级浏

览器才有,所以在低级的浏览器中需要用轮询来检测URL是否在变化,这个这里就不具体说了。


二、通过HTML5加强型的History对象实现(类Pjax)

可以通过window.history.pushState这个方法无刷新的更新浏览器地址栏,这个方法在更新地址栏的同时将地址压入历史记录堆栈里,而要取出这个栈顶页面则可以用popstate这个事件来捕获~

来模拟一下github的环境,github中每个url是对应一个完整的实际页面的,所以在ajax请求页面时需要异步获取target页面中指定id容器中的内容:

比如有这样两个页面:

index.html

Html代码 复制代码 收藏代码
  1. <!DOCTYPE HTML> 
  2. <html> 
  3.     <head> 
  4.         <meta http-equiv="Content-Type" content="text/html; charset=gbk"> 
  5.         <title>index</title> 
  6.     </head> 
  7.     <body> 
  8.         <script>document.write(new Date());</script> 
  9.         <div id="cn"> 
  10.             <a href="second.html">加载前</a> 
  11.         </div> 
  12.     </body> 
  13. </html> 
<!DOCTYPE HTML>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=gbk">
        <title>index</title>
    </head>
    <body>
    	<script>document.write(new Date());</script>
    	<div id="cn">
        	<a href="second.html">加载前</a>
        </div>
    </body>
</html>


second.html


Html代码 复制代码 收藏代码
  1. <!DOCTYPE HTML> 
  2. <html> 
  3.     <head> 
  4.         <meta http-equiv="Content-Type" content="text/html; charset=gbk"> 
  5.         <title>second</title> 
  6.     </head> 
  7.     <body> 
  8.         <script>document.write(new Date());</script> 
  9.         <div id="cn"> 
  10.             <a href="index.html">加载后</a> 
  11.         </div> 
  12.     </body> 
  13. </html> 
<!DOCTYPE HTML>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=gbk">
        <title>second</title>
    </head>
    <body>
    	<script>document.write(new Date());</script>
        <div id="cn">
        	<a href="index.html">加载后</a>
        </div>
    </body>
</html>

假如用同步的http请求打开的话完全是两个页面,两个页面加入很多地方一样的话我们完全可以用这种方法来实现ajax请求变更DOM,我在这里加了<script>document.write(new Date());</script>语句通过它的变化能得知是否取自两个http请求,局

部的ajax请求是不会改变这个时间显示的。

Js代码 复制代码 收藏代码
  1. $(function(){ 
  2.         var state = { 
  3.                     title: "index"
  4.                     url: "index.html" 
  5.                 }; 
  6.                 $("#cn").click(function(){ 
  7.                     window.history.pushState(state, "index", "second.html"); 
  8.                     var $self = $(this); 
  9.                     $.ajax({ 
  10.                         url:"second.html"
  11.                         dataType: "html"
  12.                         complete: function(jqXHR, status, responseText){ 
  13.                             responseText = jqXHR.responseText; 
  14.                             if (jqXHR.isResolved()) { 
  15.                                 jqXHR.done(function(r){ 
  16.                                     responseText = r; 
  17.                                 }); 
  18.                                 $self.html($("<div>").append(responseText.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "")).find("#cn")); 
  19.                             } 
  20.                         } 
  21.                     }); 
  22.                     document.title = "second"
  23.                     return false
  24.                 }); 
  25.                 $(window).bind('popstate', function(e){ 
  26.                     var st = e.state; 
  27.                     //$("#cn").load(st.url + " #cn"); 
  28.                     $.ajax({ 
  29.                         url:"index.html"
  30.                         dataType: "html"
  31.                         complete: function(jqXHR, status, responseText){ 
  32.                             responseText = jqXHR.responseText; 
  33.                             if (jqXHR.isResolved()) { 
  34.                                 jqXHR.done(function(r){ 
  35.                                     responseText = r; 
  36.                                 }); 
  37.                                 $("#cn").html($("<div>").append(responseText.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "")).find("#cn")); 
  38.                             } 
  39.                         } 
  40.                     }); 
  41.                     document.title = e.state.title; 
  42.                 }); 
  43. }); 

上面语句中当#cn元素被点击时将state通过pushState方法压入历史记录栈,并在第三个参数中将浏览器URL框中指向second页面,并通过ajax将second页面异步载入,将相应的部分加入容器中,这样就实现了异步加载并改变地址栏url了,同样用户点击后退时,触发popstate,刚才pushState方法中的第一个参数state便是这里传入的形参e中的state属性,通过var st = e.state取出供开发使用。同时载入index页面中对应内容。时间有限这个js没有进行重构,直接写$.ajax了,其实假如不需要任何特效单纯的异步载入在jQ中可以直接用$("#cn").load(st.url + " #cn");将请求的html对应的#cn放到本页面的#cn容器中,但加入要更加炫的特效的话就要直接操作ajax传回的数据了。

Js代码 复制代码 收藏代码
  1. $("#cn").html($("<div>").append(responseText.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "")).find("#cn")); 

先创建一个div容器在将经过script过滤过的代码装入这个容器在通过find方法找到里面对应的选择器容器插入本身的页面中,这里可以不用html来填充,可以根据自己的项目需要用slideUp,show什么的特效进行内容显示~~


另外这里要推荐一个jQuery组件叫pjax(https://github.com/defunkt/jquery-pjax),比较牛叉的一个组件,异步的部分load进来另外一个页面对应容器中的内容,实现的机理和我上面的第二种方案一致。pushState + ajax = pjax 感觉这个应用会热起来的。


稍微总结下,两种方案其实对于想IE6或者FF3.6等比较低级的浏览器支持不是很好,前者若要兼容低端浏览器要用轮询来监听浏览器地址栏行为,而后者的话是完全的HTML5应用,对于非HTML5浏览器只能做判断跳转了。

如pjax最后的一段无奈的兼容处理:

Js代码 复制代码 收藏代码
  1. $.support.pjax = window.history && window.history.pushState 
  2.  
  3.  
  4. // Fall back to normalcy for older browsers. 
  5. if ( !$.support.pjax ) { 
  6.   $.pjax = function( options ) { 
  7.     window.location = $.isFunction(options.url) ? options.url() : options.url 
  8.   } 
  9.   $.fn.pjax = function() { return this
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值