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)。
开发者可以提供一个完全的historyData 的javascript对象,用嵌套的对象objects和排列arrays来描绘复杂的状态。只要是JSON (JavaScript Object Notation) 允许的那么在历史数据里就是允许的,包括简单数据类型和null型。DOM的对象和可编程的浏览器对象比如XMLHttpRequest ,不会被保存。注意historyData 不会被书签持久化,如果浏览器关掉,或者浏览器的缓存被清空,或者用户清除历史的时候,会消失掉。
使用dhtmlHistory 最后一步,是isFirstLoad() 方法。如果你导航到一个web页面,再跳到一个不同的页面,然后按下回退按钮返回起始的网站,第一页将完全重新装载,并激发onload事件。这样能产生破坏性,当代码在第一次装载时想要用某种方式初始化页面的时候,不会再刷新页面。isFirstLoad() 方法让区别是最开始第一次装载页面,还是相对的,在用户导航回到他自己的浏览器历史中记录的网页时激发load事件,成为可能。
在例子代码中,我们只想在第一次页面装载的时候加入历史事件,如果用户在第一次装载后,按回退按钮返回页面,我们就不想重新加入任何历史事件。
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);
让我们继续使用historyStorage 类。类似dhtmlHistory ,historyStorage通过一个叫historyStorage的单一全局对象来显示他的功能,这个对象有几个方法来伪装成一个hash table, 象put(keyName, keyValue), get(keyName), and hasKey(keyName).键名必须是字符,同时键值可以是复杂的javascript对象或者甚至是xml格式的字符。在我们源码source code的例子中,我们put() 简单的XML 到historyStorage 在页面第一次装载时。
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);
}
然后,如果用户从这个页面漂移走(导航走)又通过返回按钮返回了,我们可以用get()提出我们存储的值或者用haskey()检查他是否存在。
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.)
示例2
我们的第2个例子是一个简单的模拟ajax email 应用的示例,叫O'Reilly Mail,类似Gmail. O'Reilly Mail描述了怎样使用dhtmlHistory类去控制浏览器的历史,和怎样使用historyStorage对象去缓存历史数据。
O'Reilly Mail 用户接口(user interface)有两部分。在页面的左边是一个有不同email文件夹和选项的菜单,例如 收件箱,草稿,等等。当一个用户选择了一个菜单项,比如收件箱,我们用这个菜单项的内容更新右边的页面。在一个实际应用中,我们会远程取得和显示选择的信箱内容,不过在O'Reilly Mail里,我们简单的显示选择的选项。
O'Reilly Mail使用Really Simple History 框架向浏览器历史里加入菜单变化和更新地址栏,允许用户利用浏览器的回退和前进按钮对应用做书签和跳到上一个变化的菜单。
我们加入一个特别的菜单项,地址簿,来描绘historyStorage 能够怎样被使用。地址簿是一个由联系的名字电子邮件和地址组成的javascript数组,在一个真实的应用里我们会取得他从一个远程的服务器。不过,在O'Reilly Mail里,我们在本地创建这个数组,加入几个名字电子邮件和地址,然后把他们存储在historyStorage 对象里。如果用户离开了这个web页面以后又返回的话,O'Reilly Mail应用重新从缓存里得到地址簿,胜过(不得不)再次访问远程服务器。
地址簿是在我们的初始化initialize()方法里存储和重新取得的
/** 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 buttonhistoryStorage.put("addressBook",addressBook);
}
else
{
// fetch the cached address book from
// the history storage window.addressBook =historyStorage.get("addressBook");
}
处理历史变化的代码是简单的。在下面的代码中,当用户不论按下回退还是前进按钮handleHistoryChange 都被调用。我们得到新的地址(newLocation) 使用他更新我们的用户接口来改变状态,通过使用一个叫displayLocation的O'Reilly Mail的工具方法。
/** 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;}
演示(Demo)O'Reilly Mail或者下载(download)O'Reilly Mail的源代码。
结束语
你现在已经学习了使用Really Simple History API 让你的AJAX应用响应书签和前进回退按钮,而且有代码可以作为创建你自己的应用的素材。我热切地期待你利用书签和历史的支持完成你的AJAX创造。