開源的Really Simply History(RSH)架構解決了這些問題,他帶來了AJAX應用的作書籤和控制前進後退按鈕的功能。RSH目前還是beta版,在Firefox1.0上,Netscape7及以上,和IE6及以上運行。Safari現在還不支援(要得到更詳細的說明,請看我的weblog中的文章Coding in Paradise: Safari: No DHTML History Possible).
目前存在的幾個AJAX架構可以協助我們做書籤和發布曆史,然而所有的架構都因為他們的實現而被幾個重要的bug困擾(請看Coding in Paradise: AJAX History Libraries 得知詳情)。此外,許多AJAX曆史框架組成綁定到較大的庫上,比如Backbase 和 Dojo,這些架構提供了與傳統AJAX應用不同的編程模型,強迫開發人員去採用一整套全新的方式去獲得瀏覽器的曆史相關的功能。
indow.onload = initialize; function initialize() { // initialize the DHTML History // framework dhtmlHistory.initialize(); // subscribe to DHTML history change // events dhtmlHistory.addListener(historyChange);
window.onload = initialize; function initialize() { // initialize the DHTML History // framework dhtmlHistory.initialize(); // subscribe to DHTML history change // events dhtmlHistory.addListener(historyChange); // if this is the first time we have // loaded the page... if (dhtmlHistory.isFirstLoad()) { debug("Adding values to browser " + "history", false); // start adding history dhtmlHistory.add("helloworld", "Hello World Data"); dhtmlHistory.add("foobar", 33); dhtmlHistory.add("boobah", true); var complexObject = new Object(); complexObject.value1 = "This is the first value"; complexObject.value2 = "This is the second data"; complexObject.value3 = new Array(); complexObject.value3[0] = "array 1"; complexObject.value3[1] = "array 2"; dhtmlHistory.add("complexObject", complexObject);
在add()方法被調用後,新地址立刻被作為一個錨值顯示在使用者的瀏覽器的URL欄裡。例如,一個AJAX web頁面停留在http://codinginparadise.org/my_ajax_app,調用了dhtmlHistory.add("helloworld", "Hello World Data" 後,使用者將在瀏覽器的URL欄裡看到下面的地址
http://codinginparadise.org/my_ajax_app#helloworld
然後他們可以把這個頁面做成書籤,如果他們使用這個書籤,你的AJAX應用可以讀出#helloworld值然後使用她去初始化web頁面。Hash裡的地址值被Really Simple History 架構顯式的編碼和解碼(URL encoded and decoded) (這是為瞭解決字元的編碼問題)
對當AJAX地址改變時儲存更多的複雜的狀態來說,historyData 比一個更容易的匹配一個URL的東西更有用。他是一個可選的值,可以是任何javascript類型,比如Number, String, 或者 Object 類型。有一個例子是用這個在一個多文字編輯器(rich text editor)儲存所有的文本,例如,如果使用者從這個頁面漂移(或者說從這個頁面導航到其他頁面,離開了這個頁面)走。當一個使用者再回到這個地址,瀏覽器會把這個對象返回給曆史改變接聽程式(history change listener)。
window.onload = initialize; function initialize() { // initialize the DHTML History // framework dhtmlHistory.initialize(); // subscribe to DHTML history change // events dhtmlHistory.addListener(historyChange); // if this is the first time we have // loaded the page... if (dhtmlHistory.isFirstLoad()) { debug("Adding values to browser " + "history", false); // start adding history dhtmlHistory.add("helloworld", "Hello World Data"); dhtmlHistory.add("foobar", 33); dhtmlHistory.add("boobah", true); var complexObject = new Object(); complexObject.value1 = "This is the first value"; complexObject.value2 = "This is the second data"; complexObject.value3 = new Array(); complexObject.value3[0] = "array 1"; complexObject.value3[1] = "array 2"; dhtmlHistory.add("complexObject", complexObject);
window.onload = initialize; function initialize() { // initialize the DHTML History // framework dhtmlHistory.initialize(); // subscribe to DHTML history change // events dhtmlHistory.addListener(historyChange); // if this is the first time we have // loaded the page... if (dhtmlHistory.isFirstLoad()) { debug("Adding values to browser " + "history", false); // start adding history dhtmlHistory.add("helloworld", "Hello World Data"); dhtmlHistory.add("foobar", 33); dhtmlHistory.add("boobah", true); var complexObject = new Object(); complexObject.value1 = "This is the first value"; complexObject.value2 = "This is the second data"; complexObject.value3 = new Array(); complexObject.value3[0] = "array 1"; complexObject.value3[1] = "array 2"; dhtmlHistory.add("complexObject", complexObject); // cache some values in the history // storage debug("Storing key 'fakeXML' into " + "history storage", false); var fakeXML = '<?xml version="1.0" ' + 'encoding="ISO-8859-1"?>' + '<foobar>' + '<foo-entry/>' + '</foobar>'; historyStorage.put("fakeXML", fakeXML); }
window.onload = initialize; function initialize() { // initialize the DHTML History // framework dhtmlHistory.initialize(); // subscribe to DHTML history change // events dhtmlHistory.addListener(historyChange); // if this is the first time we have // loaded the page... if (dhtmlHistory.isFirstLoad()) { debug("Adding values to browser " + "history", false); // start adding history dhtmlHistory.add("helloworld", "Hello World Data"); dhtmlHistory.add("foobar", 33); dhtmlHistory.add("boobah", true); var complexObject = new Object(); complexObject.value1 = "This is the first value"; complexObject.value2 = "This is the second data"; complexObject.value3 = new Array(); complexObject.value3[0] = "array 1"; complexObject.value3[1] = "array 2"; dhtmlHistory.add("complexObject", complexObject); // cache some values in the history // storage debug("Storing key 'fakeXML' into " + "history storage", false); var fakeXML = '<?xml version="1.0" ' + 'encoding="ISO-8859-1"?>' + '<foobar>' + '<foo-entry/>' + '</foobar>'; historyStorage.put("fakeXML", fakeXML); } // retrieve our values from the history // storage var savedXML = historyStorage.get("fakeXML"); savedXML = prettyPrintXml(savedXML); var hasKey = historyStorage.hasKey("fakeXML"); var message = "historyStorage.hasKey('fakeXML')=" + hasKey + "<br>" + "historyStorage.get('fakeXML')=<br>" + savedXML; debug(message, false);}
prettyPrintXml() 是一個第一在例子源碼full example source code中的工具方法。這個方法準備簡單的xml顯示在web page ,方便調試。
注意資料只是在使用頁面的曆史時被持久化,如果瀏覽器關閉了,或者使用者開啟一個新的視窗又再次鍵入了ajax應用的地址,曆史資料對這些新的web頁面是停用。曆史資料只有在用前進或回退按鈕時才被持久化,而且在使用者關閉瀏覽器或清空緩衝的時候會消失掉。想真正的長時間的持久化,請看Ajax MAssive Storage System (AMASS).
我們的簡單樣本已經完成。示範他(Demo it)或者下載全部的原始碼(download the full source code.)
/** Our function that initializes when the page is finished loading. */function initialize() { // initialize the DHTML History framework dhtmlHistory.initialize(); // add ourselves as a DHTML History listener dhtmlHistory.addListener(handleHistoryChange); // if we haven't retrieved the address book // yet, grab it and then cache it into our // history storage if (window.addressBook == undefined) { // Store the address book as a global // object. // In a real application we would remotely // fetch this from a server in the // background. window.addressBook = ["Brad Neuberg 'bkn3@columbia.edu'", "John Doe 'johndoe@example.com'", "Deanna Neuberg 'mom@mom.com'"]; // cache the address book so it exists // even if the user leaves the page and // then returns with the back button historyStorage.put("addressBook", addressBook); } else { // fetch the cached address book from // the history storage window.addressBook = historyStorage.get("addressBook"); }
/** Handles history change events. */function handleHistoryChange(newLocation, historyData) { // if there is no location then display // the default, which is the inbox if (newLocation == "") { newLocation = "section:inbox"; } // extract the section to display from // the location change; newLocation will // begin with the word "section:" newLocation = newLocation.replace(/section\:/, ""); // update the browser to respond to this // DHTML history change displayLocation(newLocation, historyData);}/** Displays the given location in the right-hand side content area. */function displayLocation(newLocation, sectionData) { // get the menu element that was selected var selectedElement = document.getElementById(newLocation); // clear out the old selected menu item var menu = document.getElementById("menu"); for (var i = 0; i < menu.childNodes.length; i++) { var currentElement = menu.childNodes[i]; // see if this is a DOM Element node if (currentElement.nodeType == 1) { // clear any class name currentElement.className = ""; } } // cause the new selected menu item to // appear differently in the UI selectedElement.className = "selected"; // display the new section in the right-hand // side of the screen; determine what // our sectionData is // display the address book differently by // using our local address data we cached // earlier if (newLocation == "addressbook") { // format and display the address book sectionData = "<p>Your addressbook:</p>"; sectionData += "<ul>"; // fetch the address book from the cache // if we don't have it yet if (window.addressBook == undefined) { window.addressBook = historyStorage.get("addressBook"); } // format the address book for display for (var i = 0; i < window.addressBook.length; i++) { sectionData += "<li>" + window.addressBook[i] + "</li>"; } sectionData += "</ul>"; } // If there is no sectionData, then // remotely retrieve it; in this example // we use fake data for everything but the // address book if (sectionData == null) { // in a real application we would remotely // fetch this section's content sectionData = "<p>This is section: " + selectedElement.innerHTML + "</p>"; } // update the content's title and main text var contentTitle = document.getElementById("content-title"); var contentValue = document.getElementById("content-value"); contentTitle.innerHTML = selectedElement.innerHTML; contentValue.innerHTML = sectionData;}