天天看點

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

想法與目标

另外,我也曾經見過把後退按鈕禁用的做法(其實這樣對于解決問題的确不錯),不過這些都似乎隻是一個workaround,設法避開這個AJAX應用普 遍存在的問題。似乎Gmail能夠支援Back按鈕,但是我驚奇的發現,在點選Back後,卻不能使用Forward,是以這還不算成功的解決這個問題。 那麼能否解決?似乎已經有了一定的實作。

HistoryControl是一個Server控件,必須配合UpdatePanel使用,并沒有對于Atlas的用戶端應用甚至普通的AJAX應用提供基本的支援。

不支援FireFox(不知為何,我在自己嘗試之後覺得支援FireFox比IE容易實作)。

在IE裡使用時,從Back和Forward的下拉框裡可以看出,那些Title都成為了“Empty Page”。

不支援在Back和Forward下拉框中選擇一項History跳轉。

如果通路了别的站點再Back,則在IE下不支援多次回退。

部署麻煩。事實上我覺得很奇怪,我除了直接在他的項目中成功運作之外。部署到别的項目或者是我的空間都有問題,怎麼也找不出原因,估計是檔案路徑問題,需要仔細讀一下他的代碼。  總之,這個解決方案還很不成熟,但是我們要對Nikhil,Atlas和微軟有信心,對于Back/Forward的内置支援應該會出現在Atlas的後續版本中。

于是我想,不如我來實作一個自己的吧,雖然我一直提倡軟體複用,但是如果找不到成熟的解決方案,那麼就該發揮程式員的主觀能動性了。對于我最後的實作,它有以下特點:

一個輕量級的JS解決方案。雖然我是在Atlas的基礎上寫的,但是隻是使用了Atlas中的Sys.Timer類,很容易修改成獨立于任何庫的JS代碼。 

支援IE和FireFox。

簡 單的支援Back和Forward的下拉框裡的Title文字,在大多數情況下不會出錯。産生Nikhil的這個問題的原因在闡述我的實作時會提及。我簡 單地解決了這個問題,但是沒有設計出完整支援title問題的完美實作。我有一些想法,似乎十分複雜,在嘗試時都宣告失敗。

支援在Back和Forword下拉框中選擇一項History跳轉。

支援Bookmark,使用者可以輕松将頁面加入收藏夾。 

易于使用,部署簡單。  對于我列出Nikhil的實作裡的第5個問題,我想了一些辦法,卻依舊沒有解決。現在雖然腦子裡有想法,但還需要繼續嘗試。

思路與設計

AJAX是個神奇的東西。因為有了XMLHttpRequest對象,我們能夠“不知不覺”地與伺服器端交換資料,在改變頁面顯示和行為的同時,讓使用者 感覺不到頁面的Reload。雖然在上個世紀微軟已經在早期IE裡就以ActiveX的形式提供了這個對象,并且在OWA中将其進行大量使用,但是我對于 這個對象的了解卻在AJAX大行其道之後。在這之前,為了達到類似AJAX的良好使用者體驗,往往會在頁面中放一個隐藏的IFrame,然後通過Form向 其中POST/GET資料,或者直接修改IFrame的src屬性以達到傳輸資料的效果。如果在IFrame中的頁面裡寫JS代碼,就能通過 window.parent.XXX來調用父級頁面的對象或方法,并可以通路整個DOM(當然跨Frame操作的話需要兩張頁面在同一個Domain中, 這個就是IFrame sandbox,如果了解Windows Live Gadget和Windows Live Spaces Gadget的人就能體會到在安全性友善IFrame起的重要作用)。

當時發現,隻要改變IFrame裡的位址,不論是 POST/GET還是改變其src屬性,大都會在浏覽器的History裡留下痕迹。這是如果使用者點選浏覽器的Back按鈕,則會從IFrame裡的 History裡Load以前的頁面,當然也會按照那張頁面的邏輯解釋執行其中的JS代碼。善于利用這點的話,就會産生父頁面Back/Forward的 效果。可惜當時沒有去想這一點,而且當時因為某些問題,使用者點選Back/Forward時反而會産生異常的行為,甚是麻煩。

與 POST/GET相比,改變IFrame的src屬性相對簡單,也容易操作。但是在IE重要注意的是,并不是任意修改src時都會使IFrame被加入 History。修改src的時候其實改變了IFrame裡的location。location是window的一個屬性,它分幾個部分,這裡需要提到 的就是它的href,search和hash。舉個例子,對于一個location“[url]http://www.sample.com?a=b&c=[/url] d#hello”來說,location.href是“[url]http://www.sample.com[/url]”,location.search是“?a= b&c=d”(可以看出,search其實就是Query String),location.hash是“#hello”。在IE中改變location.hash是不會影響History的,是以隻有改變 href與search才行。在FireFox中,改變hash值是可以影響浏覽器的History,但是點選Back/Forward并不會使浏覽器重 新執行頁面中的JS代碼。

解決Back/Forward的大緻方向有了,那麼Bookmark呢?應該很容易将問題變成,如何要改變浏覽器位址欄的值,但是不重新整理頁面。還好我們有hash。所有的辨別都要通過hash值來傳遞。

到現在為止,應該已經能夠實作了在不重新整理頁面時改變浏覽器的History紀錄,但是如何在使用者點選Back/Forward的時候也改變頁面内容呢?我們将這個問題分為兩部分考慮,依次解決:

在使用者點選浏覽器的Back/Forward或者選擇History中某一項時改變浏覽器的位址欄資訊。 

根據位址欄資訊的改變得到資訊,然後修改頁面的内容。    對于第1個問題,在FireFox下很容易解決,因為其實這是浏覽器已經支援的功能。如果是IE,我們隻能通過IFrame裡的頁面來改變父視窗的 hash了。第2個問題比較麻煩,因為改變浏覽器的hash值并不會觸發一個事件,是以迄今為止似乎所有的解決方案都是使用timer來不停地查詢 hash值有沒有改變。我的解決方案也不例外。

解釋完了思路與設計,就該轉向真正的實作了吧。

實作與分析

Default.aspx

 1

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

<%@ Page Language="C#" %>

 2

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

 3

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

 4

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

 5

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

<script runat="server">

 6

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

 7

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

</script>

 8

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

 9

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

<html xmlns="http://www.w3.org/1999/xhtml" >

10

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

<head runat="server">

11

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    <title>Nav Fix</title>

12

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    <script language="javascript">

13

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        var historyObj = null;

14

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        var previousIndex = 0;

15

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

16

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        function onChange(select)

17

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        {

18

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

            historyObj.addHistory(select.selectedIndex);

19

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        }

20

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

21

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        function setPageData(context)

22

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

23

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

            var select = document.getElementById("select");

24

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

            if (context)

25

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

            {

26

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

                select.selectedIndex = context;

27

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

                var request = new Sys.Net.WebRequest();

28

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

                request.set_url("Selection.ashx?s=" + select.selectedIndex);

29

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

                request.completed.add(onComplete);

30

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

                request.invoke();

31

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

            }

32

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

            else

33

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

34

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

                select.selectedIndex = 0;

35

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

                document.getElementById("message").innerHTML = "";

36

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

37

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

38

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

39

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        function onComplete(sender)

40

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

41

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

            document.getElementById("message").innerHTML = sender.get_data();

42

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

43

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

44

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        function init()

45

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

46

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

            historyObj = new Jeffz.Framework.History(setPageData, "NavFixHelper.htm");

47

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

            historyObj.start();

48

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

49

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    </script>

50

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

</head>

51

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

<body style="font-family: Arial;">

52

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    <form id="form1" runat="server">

53

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    <div>

54

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        <atlas:ScriptManager ID="ScriptManager1" runat="server" EnableScriptComponents="true">

55

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

            <Scripts>

56

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

                <atlas:ScriptReference Path="js/History.js" />

57

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

            </Scripts>

58

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        </atlas:ScriptManager>

59

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

60

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        <script type="text/xml-script">

61

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

            <page xmlns:script="[url]http://schemas.microsoft.com/xml-script/2005[/url]">

62

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

                <components>

63

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

                    <application load="init" />

64

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

                </components>

65

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

            </page>

66

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        </script>

67

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

68

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        <select onchange="onChange(this)" id="select">

69

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

            <option></option>

70

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

            <option value="1">selection 1</option>

71

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

            <option value="2">selection 2</option>

72

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

            <option value="3">selection 3</option>

73

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        </select>

74

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        <div id="message" style="font-size: 32px;"></div>

75

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    </div>

76

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    </form>

77

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

</body>

78

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

</html>

79

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

Selection.ashx

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

<%@ WebHandler Language="C#" Class="Selection" %>

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

using System;

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

using System.Web;

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

public class Selection : IHttpHandler {

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    public void ProcessRequest (HttpContext context) {

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        string value = String.Format(

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

            "You select: <strong>selection {0}</strong>", 

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

            context.Request.QueryString["s"]);

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        context.Response.Write(value);

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        context.Response.End();

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    }

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    public bool IsReusable {

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        get {

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

            return true;

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

}

   首先Application在Load之後會立即調用init方法,構造一個Jeffz.Framework.History對象 historyObj,需要傳入更新資料的回調函數,還有為了IE單獨提供的NavFixHelper.htm檔案的路徑。然後調用對象的start方法 開啟對于hash值的監聽。對象還提供了一個stop來停止監聽。我提供這兩個方法的目的是為了能夠在需要時停止timer,友善調試。需要更新頁面内容 時,如果要保留History,那麼必須通過historyObj的addHistory來提供修改所需要使用的參數context。context可以 為任意對象,将會被序列化之後被放置在位址欄的hash中,然後在構造historyObj時傳入的回調函數(setPageData)會被執行, context會被作為參數傳入回調函數。使用historyObj時,對于頁面的修改都應該放在setPageData中。在使用者通過點選 Back/Forward Button或者直接選擇History的某一項時,位址欄中的hash會改變,回調函數會獲得從目前hash得到的context作為參數,将頁面更新 至之前的狀态。

在setPageData被調用時,<select />的選項會被更改,然後會使用Sys.Net.WebRequest向Selection.ashx發送請求。Selection.ashx根據 Query String的值來傳回資訊。然後Sys.Net.WebRequest對象在收到response後修改message的資訊。

在使用上就是這麼簡單。

History.js - Constructor

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

Jeffz.Framework.History = function(setDataCallback, helperPageUrl)

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

{

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    if (!setDataCallback || (typeof setDataCallback != "function"))

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    {

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        throw new Error("Please provide a callback function");

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    this.__setDataCallback = setDataCallback;

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    this.__currentHash = null;

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    this.__helperIFrame = null;

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    this.__helperPageUrl = helperPageUrl;

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    this.__runtimeTimer = null;

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    if (Sys.Runtime.get_hostType() == Sys.HostType.InternetExplorer)

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        if (!helperPageUrl)

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

            throw new Error("Please provide the helper page for IE.");

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        else

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

            var helperIFrame = document.createElement("iframe");

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

            helperIFrame.style.display = "none";

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

            document.body.appendChild(helperIFrame);

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

            this.__helperIFrame = helperIFrame;

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

            this.__reloadHelperIFrame(location.hash);

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    else

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        this.__currentHash = location.hash;

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        this.__execute(location.hash);

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

  對于所有的私有成員,我都使用成員名前加上“__”的方法,加以區分。

首先,傳入的第一個參數setDataCallback必須是一個函數,否則将抛出異常。緊接着判斷浏覽器類型,如果是IE,則檢測必須提供 helperPageUrl,并且構造一個隐藏的IFrame來輔助實作history功能,最後通過 this.__reloadHelperIFrame方法來修改位址欄裡的hash。如果不是IE,那麼就直接将this.__currentHash設 成目前的hash加以記錄,并調用this.__execute函數将hash值構造成context對象,并執行回調函數 this.__setDataCallback。

History.js - __reloadHelperIFrame

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

Jeffz.Framework.History.prototype.__reloadHelperIFrame = function(hash)

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    this.__helperIFrame.document.title = document.title;

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    if (this.__helperIFrame.src && this.__helperIFrame.src.indexOf("?true") >= 0)

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        this.__helperIFrame.src = this.__helperPageUrl + "?false&" + document.title + hash;

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        this.__helperIFrame.src = this.__helperPageUrl + "?true&" + document.title + hash;

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

NavFixHelper.htm

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

<head>

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    <title>Untitled Page</title>

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

<body>

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        window.parent.location.hash = location.hash;

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        var queryString = location.search;

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        var index = queryString.indexOf("&");

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        document.title = queryString.substring(index + 1);

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

  把__reloadHelperIFrame函數和NavFixHelper.htm頁面同時分析,是因為它們兩個有密不可分的關系。它們都是僅僅為了IE服務的。

在前面的解釋裡,我已經談到了改變iframe的Query String(search)可以将狀态添加至浏覽器的History中。于是__reloadHelperIFrame函數保證了新加載的Query String,和目前IFrame裡頁面的Query String不同(如果目前src包含了“&true”字元,那麼就加載包含“&false”的位址,否則就加載包含“& false”的位址)。接着在新加載的NavFixHelper.htm頁面中,會将父視窗的hash變成自己的hash。這樣,浏覽器位址欄裡的值就會 改變了。這樣,當浏覽器的Back/Forward Button被點選,或者使用者直接從History清單裡選擇一項時,IFrame裡的頁面會被重新加載,并且會使用特定的位址,頁面的JS代碼會被重新 執行,最終又将影響浏覽器位址欄裡的hash值。不過由于如果通路了其他頁面,History隻能記住最後一次IFrame裡的位址,是以無法使用 Back多次回退了。

每次加入History項,或者重新通路History時,都須要重新加載一次 NavFixHelper.htm,那麼會不會影響性能?答案是否定的。首先在加入History時,使用的href+search值隻會用兩種,是以浏 覽器會幫助cache檔案内容,第二次通路具有相同href+search的頁面時浏覽器将不再通路Server。而且在伺服器端,對于這樣的靜态檔案, 也會盡可能的進行cache,是以即使通路了Server也會獲得304(未改變,表示從緩存讀取)資訊,而不會從伺服器端重新下載下傳。再退一步,重新下載下傳 一個如此之小的檔案,也不會對性能有多少影響。

可以發現,在加載IFrame的頁面時,還将目前頁面的Title傳入了Query String。其作用就是避免Nikhil的解決方案中我舉出的第三個問題。NavFixHelper.htm會根據位址欄資訊修改自身的titile, 這樣在IE添加History項時,會紀錄IFrame裡頁面的title,這樣就保證了浏覽器History清單裡的title與浏覽器頁面相同。可是 這樣還沒有完美的解決Title問題,如果使用者想要修改頁面的Title,IE裡我有了解決方案,卻始終還沒有找出在FireFox下正确紀錄的方法,總 是在Back/Forward之中History裡的title就混亂了,似乎在FireFox裡Forword/Back記錄title的行為和IE不 一樣,讓我百思不得其解。

History.js - addHistory

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

Jeffz.Framework.History.prototype.addHistory = function(context)

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    historyEntry = { __p__ : context };

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        this.__reloadHelperIFrame("#" + encodeURI(Sys.Serialization.JSON.serialize(historyEntry)));

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        location.hash = encodeURI(Sys.Serialization.JSON.serialize(historyEntry));

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

  雖然可以說addHistory方法是關鍵,但是其實卻非常的簡單,在IE下則使用this.__reloadHelperIFrame來加載頁面,否則就直接修改hash。hash的值使用的就是使用者傳入的context,序列化後,并經過encodeURI處理。

History.js - start & stop

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

Jeffz.Framework.History.prototype.start = function()

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    if (!this.__runtimeTimer)

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        var timer = new Sys.Timer();

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        timer.set_interval(300);

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        timer.tick.add(this.__onTimerTick);

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        timer.historyObj = this;

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        this.__runtimeTimer = timer;

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    if (this.__runtimeTimer.get_enabled())

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        throw new Error("The history object has been started.");

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        this.__runtimeTimer.set_enabled(true);

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

Jeffz.Framework.History.prototype.stop = function()

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    if (!this.__runtimeTimer || !this.__runtimerTimer.get_enabled())

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        throw new Error("The history object has not been started.");

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        this.__runtimeTimer.set_enabled(false);

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

   這兩個函數非常簡單,本來不用解釋。唯一需要注意的是代碼的第8行,由于Sys.Timer使用的是使用setTimerout來回調執行 this.__onTimerTick,是以在this.__onTimerTick函數執行時,this并不是History對象本身,而是 window!還好Sys.Timer會通過sender傳遞自身給回調函數,是以我在構造Sys.Timer時将自身對象放進了Timer對象的 historyObj屬性中,則在this.__onTimerTick函數中可以從sender.historyObj中得到History對象。

History.js - __onTimerTick

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

Jeffz.Framework.History.prototype.__onTimerTick = function(sender, eventArgs)

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    try

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        if (location.hash != sender.historyObj.__currentHash)

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

            sender.historyObj.__currentHash = location.hash;

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

            sender.historyObj.__execute(location.hash);

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    catch(e)

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        sender.set_enabled(false);

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        sender.set_enabled(true);

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        throw e;

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

   其實這個方法就是比較hash值有沒有改變,如果改變了,則保留新的hash,并加以執行回調函數(在__execute函數中)。可以發現,事實上回 調函數this.__setDataCallbak總是在在onTimerTick裡被執行的(除了在FireFox下構造History對象時),這樣 保證了this.__currentHash和目前頁面hash的統一。

值得注意的是,我使用了try...catch來保護了代 碼,因為__execute函數會調用使用者提供的回調函數,是以可能會抛出異常。如果__onTimerTick函數僅僅隻使用了現在的第5到第9行,那 麼等異常抛出時,Timer将會被終止,将不會繼續監視hash的改變。現在的做法是在遇到異常時停止Timer并重新啟動,然後重新抛出異常。這樣既保 證了Timer的正常執行,又能讓使用者發現自己代碼中的異常情況。

History.js - __execute

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

Jeffz.Framework.History.prototype.__execute = function(hash)

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    var historyEntry = null;

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        var historyEntry = Sys.Serialization.JSON.deserialize(decodeURI(hash.substring(1)));

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        this.__setDataCallback(null);

修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        return;

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

    if (historyEntry)

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

        this.__setDataCallback(historyEntry.__p__);

修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題
修補AJAX應用中Back/Forward Button和Bookmark失效的問題

  這段我想就不用多加解釋了,似乎将一個簡單的調用寫得有些複雜。這樣做的目的是在hash值是錯誤的情況下,會将null作為參數,保證了使用者提供的回調函數被正确的使用。

問題與其他

   就此,整個類分析完了,總共隻有短短一百多行代碼,是我參考了一些實作,并通過自己的經驗與思考得出的解決方案。目前似乎關于解決AJAX這個問題的解 決方案都有差不多的模式,也有差不多的障礙。這依舊不是一個完美的解決方案,依舊有改進的餘地。其實我這篇東西的作用隻是抛磚引玉,希望能引出更多優秀的 見解。我仍然在鑽研和實踐中,如果有什麼進展,我會第一時間的更新這裡的blog。

本文轉自 jeffz 51CTO部落格,原文連結:http://blog.51cto.com/jeffz/60959,如需轉載請自行聯系原作者

繼續閱讀