• Ajax异步刷新地址栏url改变(利用Html5 history.pushState实现)


      早些时候在博客园参阅了不少资料,然后决定入驻博客园分享自己的开发心得,最近准备转方向筹备着辞职交接工作,所以有点忙碌,搁置了一个月才匆匆写下这么一篇随笔,希望能给大家带来一点帮助吧,资料和学识有限,如有不正确的地方敬请指出,万分感谢。

      效果:

      众所周知:ajax可以实现页面的局部刷新,可以做到非常奈斯的数据加载效果,给用户带来非常良好的体验,但是ajax的除了会曝露一些不太安全的服务器信息之外,最蛋疼的就是不能在浏览器的历史会话中保留记录。就是当你点开一个页面,ajax各种数据加载各种欢乐啊,例如一个列表页面,异步加载来翻页。结果用户一不小心刷新了页面,那么页码就得重新开始计算,一旦用户改变了会话状态(浏览器的前进、后退、刷新),那么ajax就会丢失相关的数据。

      最近在网上浏览各大网站无意中发现了ajax异步刷新但是地址栏改变的效果。

      例如google doodles,大家可以点击下面的链接后翻页查看图片,就可以发现页面ajax刷新地址栏也同步改变了,飞机票在这里,请戳:

      http://www.google.com/doodles/grandfathers-day-2014

      优点如下:

    •   卓越的用户体验
    •   对搜索引擎更加友好
    •   更多敬请补充...

      实现方案(写入会话状态):

      对这种效果抱着好奇的心态就去查阅了一下资料。

      在Html5中window.history对象引入了pushStatereplaceState方法,对window.history对象还不太了解的童鞋请自行查阅相关资料。

      示例:

      

    //格式约定
    history.pushState(data, title[, url]);
    /// <summary>
    ///     pushState方法调用示例,replaceState方法同样的参数格式,
    ///   &#10; 本方法负责将自定义数据写入浏览器会话历史
    ///   &#10; linkFly原创,引用请注明出处,谢谢
    /// </summary> /// <param name="data" type="object"> /// 需要进行保存(在历史会话)的数据,该数据可以在下一次会话中读取出来 /// </param> /// <param name="title" type="String"> /// 写入历史会话的标题,经过测试暂时没有发现用处,不会对当前文档标题产生影响,可以传入空字符串 /// </param> /// <param name="url" type="String"> /// 要写入浏览器历史会话的Url【注意不允许跨域】 /// </param> /// <returns type="void" /> window.history.pushState(
        {pageIndex:1,keywords:'善了个哉'},
        document.title,
        window.location.pathname+'?pageIndex=1'
      );

      原理:

      该方法隶属Html5,是为了解决ajax方法不能“回到过去”的问题。window.history负责管理浏览器会话历史,而pushState则在浏览器历史中添加一条会话历史(replaceState则是替换一条历史)作为当前会话状态,而在用户刷新了页面之后进入这条会话,这时候我们只需要把会话的数据读取出来就行了。

      对每个参数进行特别说明一下:

    •   Data:可以任意存放一组数据,这组数据存放在该历史对话中,下次进入该对话则可以通过读取该数据来进行状态保持,例如可以存放页码信息,但是注意值不能引用对象,会报ObjectCloneError(对象克隆异常),例如当前页面的Element、$("#testId")这种引用了依赖当前页面的数据,Data数据要保持独立,值不允许为引用类型的对象
    •   Title:参阅的大部分文档对其都没有说明,只是简单的描述了文档标题。应该是在读取会话数据的时候自动对应到页面标题上,但是经过测试并不会对页面标题有任何的影响,如果是为了实现自动对应标题的话,功能就有点鸡肋(完全可以用Data),一般可以传入:document.title''(空字符串)
    •   Url:就是要写入历史会话的url,假定我们已经在历史记录中写入了Url,浏览器进行前进操作,跳转到了别的页面,然后再返回,进入的就是这个url。不允许跨域!本着面向对象的思想,一般可以通过window.location.pathname获取当前url地址,然后通过字符串拼接后面的参数传入就可以生成稳妥的url了。

      读取会话历史:

       在轻松写入了会话历史之后,我们还需要将它读出来才行。这个读取的切入点,嗯,查阅了资料说的都是通过onpopstate事件,实际上在这个事件上非常的蛋疼,我查阅的多数的文章都告诉说捕捉这个事件即可,代码如下:

        本示例不推荐使用:

    //本代码仅作参考
    //环境 Firefox 25.0.1级以下版本不推荐使用本代码(高版本尚未测试)
    window.addEventListener('popstate', function(e) {
    /// <summary>
    ///   &#10; 在页面初始化加载完成中添加该事件,则可以监听到onpopstate事件,而浏览器进行前进、后退、刷新操作都会触发本事件
    ///   &#10; linkFly原创,引用请注明出处,谢谢
    /// </summary>/// <returns type="void" />
    if (e.state) { //e.state就是pushState中保存的Data,我们只需要将相应的数据读取下来即可 } }); // 传闻可以直接使用history.state来获取当前对应的state数据,笔者尚未测试,有兴趣的可以自行研究下,注意主要测试Firefox

      注意,以上代码在Firefox下存在问题。

      在这些资料中都只是草草的告诉说onpopstate事件可以做到读取数据,但在Firefox下,页面加载中根本不会触发onpopstate事件。

      大家注意中间一大段的最后一句:

      Chrome and Safari always emit a popstate event on page load, but Firefox doesn't.

      翻译过来就是Chrome和Safari都会在页面加载中触发该事件,但是Firefox不会。

      所以这时候有两种处理方案:

    • 在页面加载中手动触发该事件
    • 通过解析url来实现,注意如果希望通过url来获取数据的话,那么之前是pushState主要需要保存的数据并非是data,而是url。这个概念需要清晰。

      在页面加载中手动触发该事件代码如下:

      

    $(function(){ 
        //通过jQuery.trigger()方法触发
        //或者自己手写js触发,具体代码这里就不贴了...
        $(window).trigger("hashchange"); 
    });

      通过解析url代码如下:

      

         function getUrlParameter(fieldName) {
            /// <summary>
            ///     1: 获取地址栏参数方法
            ///     &#10;    - getUrlParameter(fieldName) - 在当前Url中查询指定的参数,返回查询得到的值,当不支持pushState或没有查询到参数的时候返回空字符串
            /// </summary>
            /// <param name="fieldName" type="String">
            ///     要查询的字符串
            /// </param>
            /// <returns type="String" />
        if (window.history.pushState) { var urlString = document.location.search; if (urlString != null) { var typeQu = fieldName + "="; var urlEnd = urlString.indexOf(typeQu); if (urlEnd != -1) { var paramsUrl = urlString.substring(urlEnd + typeQu.length); var isEnd = paramsUrl.indexOf('&'); if (isEnd != -1) { return paramsUrl.substring(0, isEnd); } else { return paramsUrl; } } else { return ""; } } else { return ""; } } else { return ''; } } //调用方法:getUrlParameter('要查询的参数')

      总结:

      表达能力实在有限,快速总结一下。我个人采用的是url的方法来获取参数的,因为觉得这样更加稳妥,毕竟对于onpopstate中e.state琢磨的还是很透彻,而url从某种方式来更加的合理与稳妥一点。具体还需要根据实际情况来处理,采用url的方式需要服务器上对相应的url进行一番处理。

    •   window.history.pushState()方法中参数Data里面的值不允许存在和页面相关引用对象,可以这样{ pageIndex : 1 },但是不可以这样{ pageDom : document.getElementById('testId') }
    •   window.history.pushState()方法中参数url不允许跨域。
    •   目前在Firefox下onpopstate事件不会在page load中加载执行。
    •   如果后台需要区别是ajax还是经过push历史的ajax,可以在pushState中的ajax里面发送特殊的请求头,后台接收到该特殊的请求头信息后进行特殊处理。
    •   还有没补充的例如浏览器兼容性神马的请拉到文章最下面。

      其他:

      提供一份自己写的一份相应的js,直接copy使用即可。

      

    //historyState对象,提供push历史数据和获取历史数据方法。
    //linkFly原创,引用请注明出处,谢谢
    var historyState = {
        checkCanPush: function () {
            /// <summary>
            ///     检测浏览器是否支持pushState方法
            /// </summary>
            /// <returns type="Boolean" />
            if (window.history.pushState)
            
                return true;
            return false;
        },
        pushState: function (data, url) {
            /// <summary>
            ///     状态保持方法(需要高版本浏览器支持),当canPush为true的时候表示浏览器可以进行push状态,则进行状态push并返回是否成功
            ///     &#10;   1.1 - pushState(data,url) 将指定的data,和url push到浏览器会话历史进行状态保持【注意Url不允许跨域】
            /// </summary>
            /// <param name="url" type="String">
            ///     需要写入浏览器会话历史的url
            /// </param>
            /// <returns type="Boolean" />
            if (historyState.checkCanPush()) {
                //注意data虽然可以保存数据,但是不能保存仍然引用着当前页面元素的对象,例如$("DOM")这样一个对象,就会出现ObjectCloneError
                window.history.pushState(data, document.title, url);
                return true;
            }
            return false;
        },
        getUrlParameter: function (fieldName) {
            /// <summary>
            ///     1: 获取地址栏参数方法
            ///     &#10;    - getUrlParameter(fieldName) - 在当前Url中查询指定的参数,返回查询得到的值,当不支持pushState或没有查询到参数的时候返回空字符串
            /// </summary>
            /// <param name="fieldName" type="String">
            ///     要查询的字符串
            /// </param>
            /// <returns type="String" />
            if (historyState.checkCanPush()) {
                var urlString = document.location.search;
                if (urlString != null) {
                    var typeQu = fieldName + "=";
                    var urlEnd = urlString.indexOf(typeQu);
                    if (urlEnd != -1) {
                        var paramsUrl = urlString.substring(urlEnd + typeQu.length);
                        var isEnd = paramsUrl.indexOf('&');
                        if (isEnd != -1) {
                            return paramsUrl.substring(0, isEnd);
                        }
                        else {
                            return paramsUrl;
                        }
                    }
                    else {
                        return "";
                    }
                }
                else {
                    return "";
                }
            } else {
                return '';
            }
        }
    }
    
    //historyState对象调用示例
    window.onload = function () {
        //写入历史会话
        historyState.pushState({ pageIndex: 1 }, window.location.pathname + '?pageIndex=1');
        //获取历史会话的数据(获取url的参数数据)
        var pageIndex = historyState.getUrlParameter('pageIndex');
        //进行数据还原操作...
    
    }

      相关资料:

    作者:linkFly
    声明:嘿!你都拷走上面那么一大段了,我觉得你应该也不介意顺便拷走这一小段,希望你能够在每一次的引用中都保留这一段声明,尊重作者的辛勤劳动成果。
  • 相关阅读:
    codeforces 980A Links and Pearls
    zoj 3640 Help Me Escape
    sgu 495 Kids and Prizes
    poj 3071 Football
    hdu 3853 LOOPS
    hdu 4035 Maze
    hdu 4405 Aeroplane chess
    poj 2096 Collecting Bugs
    scu 4444 Travel
    zoj 3870 Team Formation
  • 原文地址:https://www.cnblogs.com/silin6/p/linkFly_pushState.html
Copyright © 2020-2023  润新知