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
聲明:嘿!你都拷走上面那么一大段了,我覺得你應該也不介意順便拷走這一小段,希望你能夠在每一次的引用中都保留這一段聲明,尊重作者的辛勤勞動成果。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM