专注于互联网--专注于架构

最新标签
网站地图
文章索引
Rss订阅

首页 »XML教程 » ajax后退:AJAX应用程序对书签和后退按钮的支持 »正文

ajax后退:AJAX应用程序对书签和后退按钮的支持

来源: 发布时间:星期四, 2009年2月12日 浏览:126次 评论:0


 本文将展示个开源JavaScript库该脚本库给AJAX应用带来了书签和后退按钮支持在学习完这个教程后开发人员将能够获得对个AJAX问题解决方案(甚至连GoogleMaps和Gmail现在都不提供该解决方案):个强大、可用书签和后退前进功能其操作行为如同其他Web应用

  本文将阐述目前AJAX应用在使用书签和后退按钮方面所面临严重问题;展示ReallySimpleHistory(RSH)库——个可以解决以上问题开源框架并提供几个运行中例子

  本文所展示这个框架主要发明分为两部分首先是个隐藏HTML表单用于缓存Cache大量短期会话客户端信息;这种缓存Cache功能为页面导航提供了强大支持其次是超链接锚点和隐藏Iframe组合它们被嵌入后退和前进按钮用来截获和记录浏览器历史记录事件以上两种技术都被包装在个简单JavaScript库中来简化开发

  问题

  书签和后退按钮在传统多页面Web应用中运行得非常好当用户浏览web站点时候其浏览器地址栏记录随新URL而更新这些记录可以被粘贴到电子邮件或者书签中供以后使用后退和前进按钮也可以正常操作使用户可以在访问过页面中向前或向后翻动

  但是AJAX应用却不它们是运行在单个Web页面中复杂浏览器并不是为这类而构建——这类Web应用已经过时它们在每次鼠标点击时候都需要重新刷新整个页面

  在这种类似于GmailAJAX软件Software中浏览器地址栏在用户选择功能和改变状态时候保持不变这使得无法在特定应用视图中使用书签此外如果用户按下“后退”按钮来“撤销”上次操作他们会惊奇地发现浏览器会完全离开该应用Web页面

  解决方案

  开源RSH框架可以解决这些问题它为AJAX应用提供了书签和控制后退、前进按钮功能RSH目前还处于Beta阶段可以在Firefox1.0、Netscape7+、InternetExplorer6+等浏览器上运行;目前还不支持Safari

  目前有几个AJAX框架对书签和历史记录问题有所帮助;但这些框架目前都有几个由于实现而造成重大Bug此外很多AJAX历史记录框架被绑定到较大库上例如Backbase和Dojo;这些框架为AJAX应用引入了完全区别编程模型迫使开发人员使用全新方式来获得历史记录功能

  相较的下RSH是个可以包含在现有AJAX系统中简单模块此外RSH库采用了些技术以避免产生影响其他历史记录框架Bug

  RSH框架由两个JavaScript类组成:DhtmlHistory和HistoryStorage

  DhtmlHistory类为AJAX应用提供历史记录抽象AJAX页面使用add思路方法添加历史记录事件到浏览器指定新地址和相关历史记录数据DhtmlHistory类使用个锚散列(如#-location)更新浏览器当前URL同时把历史记录数据和该新URL关联AJAX应用将自己注册为历史记录监听器当用户使用后退和前进按钮进行浏览时历史记录事件被触发为浏览器提供新位置以及和add起保存任何历史记录数据

  第 2个类:HistoryStorage允许开发人员保存任意数量已存历史记录数据在普通Web页面中当用户导航到个新web站点时浏览器卸载并清除web页面上所有应用和JavaScript状态;如果用户用后退按钮返回所有数据都丢失了HistoryStorage类通过个包含简单散列表思路方法(例如put、get、hasKey)API来解决这类问题上面思路方法允许开发人员在用户离开Web页面的后保存任意数量数据;当用户按后退按钮重新返回时历史记录数据可以通过HistoryStorage类来访问在内部我们通过使用隐藏表单字段来实现此功能这是浏览器会自动保存表单字段中甚至在用户离开Web页面时候也如此 [Page]

  例子

  让我们先从个简单例子开始

  首先任何需要使用RSH框架页面都必须包含dhtmlHistory.js脚本:


<!--LoadtheReallySimple
Historyframework-->
<scripttype=\"text/javascript\"
src=\"../../framework/dhtmlHistory.js\">
</script>

  DHTML历史记录应用也必须在和AJAXWeb页面相同目录下包含blank.html文件;这个文件和RSH框架打包在且对于InternetExplorer来说是必需顺便提RSH使用个隐藏Iframe来跟踪和添加InternetExplorer历史记录变化;这个Iframe需要我们指定个实际文件位置才能正常工作这就是blank.html

  RSH框架创建了个叫做dhtmlHistory全局对象这是操纵浏览器历史记录入口点使用dhtmlHistory步是在Web页面加载完成后化dhtmlHistory对象:


window.onload=initialize;

functioninitialize{
 //initializetheDHTMLHistory
 //framework
 dhtmlHistory.initialize;

  然后开发人员使用dhtmlHistory.addListener思路方法订阅历史记录变化事件这个思路方法带有个JavaScript回调当DHTML历史记录变化事件发生时接收两个参数:新页面位置以及任何可和该事件关联可选历史记录数据:


window.onload=initialize;

functioninitialize{
 //initializetheDHTMLHistory
 //framework
 dhtmlHistory.initialize;

 //subscribetoDHTMLhistorychange
 //events
 dhtmlHistory.addListener(historyChange);

  historyChange思路方法很简单在用户导航到个新位置后接收Location以及任何和该事件关联可选historyData


/**Ourcallbacktoreceivehistorychange
events.*/
functionhistoryChange(Location,
historyData){
 debug(\"Ahistorychangehasoccurred:\"


  +\"Location=\"+Location
  +\",historyData=\"+historyData,
  true);
}

  上面用到debug思路方法是定义在举例源文件中个实用它和完整举例打包在起供下载debug只是用来将消息打印到Web页面上;第 2个布尔型参数(在上述代码中值为true)控制是否在打印新调试消息的前清除原有全部消息

  开发人员使用add思路方法添加历史记录事件添加历史记录事件涉及为历史记录变化指定个新地址例如edit:SomePage以及提供个和该事件起保存可选historyData值


window.onload=initialize;

functioninitialize{
 //initializetheDHTMLHistory
 //framework
 dhtmlHistory.initialize;

 //subscribetoDHTMLhistorychange [Page]
 //events
 dhtmlHistory.addListener(historyChange);

 //thisisthefirsttimewehave
 //loadedthepage...
 (dhtmlHistory.isFirstLoad){
  debug(\"Addingvaluestobrowser\"+\"history\",false);
  //startaddinghistory
  dhtmlHistory.add(\"helloworld\",\"HelloWorldData\");
  dhtmlHistory.add(\"foobar\",33);
  dhtmlHistory.add(\"boobah\",true);

  varcomplexObject=Object;
  complexObject.value1=\"Thisisthefirstvalue\";
  complexObject.value2=\"Thisistheseconddata\";
  complexObject.value3=Array;
  complexObject.value3[0]=\".gif' />1\";
  complexObject.value3[1]=\".gif' />2\";

  dhtmlHistory.add(\"complexObject\",complexObject);

  在add的后地址将立即作为个锚值(链接地址)显示在浏览器URL地址栏中例如对地址为http://codinginparadise.org/my_ajax_appAJAXWeb页面dhtmlHistory.add(\"helloworld\",\"HelloWorldData\")的后用户将会在其浏览器URL地址栏中看到如下地址:

  http://codinginparadise.org/my_ajax_app#helloworld

  然后用户可以将这个页面做成书签如果以后用到这个书签AJAX应用可以读取#helloworld值并用它来化Web页面散列后面地址值是RSH框架可以透明编码和解码URL地址
  HistoryData非常有用它保存比简单URL更为复杂AJAX地址变化状态这是个可选值可以是任何JavaScript类型例如Number、String或Object使用该保存功能个例子是在个富文本编辑器中保存所有文本(比如在用户离开当前页面时)当用户再回到这个地址时浏览器将会将该对象返回给历史记录变化监听器

  开发人员可以为historyData提供带有嵌套对象和表示复杂状态完整JavaScript对象;JSON(JavaScriptObjectNotation)所支持在历史记录数据中都支持包括简单数据类型和null类型然而DOM对象以及可用脚本编写浏览器对象(如XMLHttpRequest)不会被保存请注意historyData并不随书签起保存当浏览器关闭浏览器缓存Cache被清空或者用户清除历史记录时候它就会消失

  使用dhtmlHistory最后步是isFirstLoad思路方法在某些浏览器中如果导航到个Web页面再跳转到另个区别页面然后按“后退”按钮返回到起始站点页将完全重新加载并触发onload事件这样会对想要在第次加载页面时用某种方式对其进行化(而其后则不使用这种方式重新加载该页面)代码造成破坏isFirstLoad思路方法可以区分是第次加载个Web页面还是用户导航到保存在历史记录中Web页面时触发“假加载”事件 [Page]

  在举例代码中我们只想在第次加载页面时候添加历史记录事件;如果用户在加载页面后按后退按钮返回该页面我们就不想重新添加任何历史记录事件:


window.onload=initialize;

functioninitialize{
 //initializetheDHTMLHistory
 //framework
 dhtmlHistory.initialize;

 //subscribetoDHTMLhistorychange
 //events
 dhtmlHistory.addListener(historyChange);

 //thisisthefirsttimewehave
 //loadedthepage...
 (dhtmlHistory.isFirstLoad){
  debug(\"Addingvaluestobrowser\"+\"history\",false);
  //startaddinghistory
  dhtmlHistory.add(\"helloworld\",\"HelloWorldData\");
  dhtmlHistory.add(\"foobar\",33);
  dhtmlHistory.add(\"boobah\",true);

  varcomplexObject=Object;
  complexObject.value1=\"Thisisthefirstvalue\";
  complexObject.value2=\"Thisistheseconddata\";
  complexObject.value3=Array;
  complexObject.value3[0]=\".gif' />1\";
  complexObject.value3[1]=\".gif' />2\";

  dhtmlHistory.add(\"complexObject\",complexObject);

  让我们继续使用historyStorage类类似于dhtmlHistoryhistoryStorage通过个叫historyStorage全局对象来公开它功能该对象有几个模拟散列思路方法比如put(keyName、keyValue)、get(keyName)和hasKey(keyName)键名称必须是同时键值可以是复杂JavaScript对象甚至是XML格式在我们源代码例子中在第次加载页面时我们使用put将简单XML放入historyStorage:




window.onload=initialize;

functioninitialize{
 //initializetheDHTMLHistory
 //framework
 dhtmlHistory.initialize;

 //subscribetoDHTMLhistorychange
 //events
 dhtmlHistory.addListener(historyChange);

 //thisisthefirsttimewehave
 //loadedthepage...
 (dhtmlHistory.isFirstLoad){
  debug(\"Addingvaluestobrowser\"+\"history\",false);
  //startaddinghistory
  dhtmlHistory.add(\"helloworld\",\"HelloWorldData\");
  dhtmlHistory.add(\"foobar\",33);
  dhtmlHistory.add(\"boobah\",true);

  varcomplexObject=Object; [Page]
  complexObject.value1=\"Thisisthefirstvalue\";
  complexObject.value2=\"Thisistheseconddata\";
  complexObject.value3=Array;
  complexObject.value3[0]=\".gif' />1\";
  complexObject.value3[1]=\".gif' />2\";

  dhtmlHistory.add(\"complexObject\",complexObject);

  //cachesomevalueshehistory
  //storage
  debug(\"Storingkey’fakeXML’o\"+\"historystorage\",false);
  varfakeXML=
   ’<?xmlversion=\"1.0\"’
   +’encoding=\"ISO-8859-1\"?>’
   +’<foobar>’
   +’<foo-entry/>’
   +’</foobar>’;
  historyStorage.put(\"fakeXML\",fakeXML);
}

  然后如果用户离开页面后又通过后退按钮返回该页面我们可以使用get思路方法提取保存或者使用hasKey思路方法检查该值是否存在


window.onload=initialize;

functioninitialize{
 //initializetheDHTMLHistory
 //framework
 dhtmlHistory.initialize;

 //subscribetoDHTMLhistorychange
 //events
 dhtmlHistory.addListener(historyChange);

 //thisisthefirsttimewehave
 //loadedthepage...
 (dhtmlHistory.isFirstLoad){
  debug(\"Addingvaluestobrowser\"+\"history\",false);
  //startaddinghistory
  dhtmlHistory.add(\"helloworld\",\"HelloWorldData\");
  dhtmlHistory.add(\"foobar\",33);
  dhtmlHistory.add(\"boobah\",true);

  varcomplexObject=Object;
  complexObject.value1=\"Thisisthefirstvalue\";
  complexObject.value2=\"Thisistheseconddata\";
  complexObject.value3=Array;
  complexObject.value3[0]=\".gif' />1\";
  complexObject.value3[1]=\".gif' />2\";

  dhtmlHistory.add(\"complexObject\",complexObject);

  //cachesomevalueshehistory
  //storage
  debug(\"Storingkey’fakeXML’o\"+\"historystorage\",false);
  varfakeXML=’<?xmlversion=\"1.0\"’+’encoding=\"ISO-8859-1\"?>’ [Page]
   +’<foobar>’+’<foo-entry/>’+’</foobar>’;
  historyStorage.put(\"fakeXML\",fakeXML);
 }

 //retrieveourvaluesfromthehistory
 //storage
 varsavedXML=historyStorage.get(\"fakeXML\");
 savedXML=prettyPrXml(savedXML);
 varhasKey=historyStorage.hasKey(\"fakeXML\");
 varmessage=\"historyStorage.hasKey(’fakeXML’)=\"
   +hasKey+\"
\"
   +\"historyStorage.get(’fakeXML’)=
\"
   +savedXML;
 debug(message,false);
}

  prettyPrXml个定义在完整举例源代码中实用思路方法;此准备在web页面中显示以便用于调试简单XML

  请注意相关数据只在该页面历史记录中进行持久化;如果浏览器被关闭或者用户打开个新窗口并再次键入AJAX应用地址则该历史记录数据对于新Web页面不可用历史记录数据只有在用到后退或前进按钮时才被持久化当用户关闭浏览器或清空缓存Cache时候就会消失如果想真正长期持久化请参阅AjaxMAssiveStorage(AMASS)

  我们简单举例已经完成
  举例2:O’ReillyMail

  我们第 2个例子是个简单AJAX电子邮件模拟应用举例即O’ReillyMail它类似于GmailO’ReillyMail描述了如何使用dhtmlHistory类来控制浏览器历史记录以及如何使用historyStorage对象来缓存Cache历史记录数据



  O’ReillyMail用户界面由两部分组成在页面左边是个带有区别电子邮件文件夹和选项菜单例如收件箱、草稿箱等当用户选择了个菜单项(如收件箱)就用这个菜单项内容更新右边页面个现实应用我们会远程获取并显示选择信箱内容不过在O’ReillyMail中我们只显示已选择选项

  O’ReillyMail使用RSH框架向浏览器历史记录中添加菜单变化并更新地址栏允许用户利用浏览器后退和前进按钮为应用做收藏书签和跳到上次变化菜单

  我们添加个特殊菜单项——地址簿以介绍说明如何来使用historyStorage地址簿是个由联系人名称和邮件地址组成JavaScript个现实应用我们会从台远程服务器取得这个不过在O’ReillyMail中我们在本地创建这个添加几个名称和电子邮件地址然后将其保存在historyStorage对象中如果用户离开Web页面后又返回该页面那么O’ReillyMail应用将重新从缓存Cache检索地址簿而不是再次联系远程服务器

  我们用initialize思路方法保存和检索地址簿:


/**Ourfunctionthatinitializeswhenthepage
isfinishedloading.*/
functioninitialize{
//initializetheDHTMLHistoryframework [Page]
dhtmlHistory.initialize;

//addourselvesasaDHTMLHistorylistener
dhtmlHistory.addListener(handleHistoryChange);

//wehaven’tretrievedtheaddressbook
//yet,grabitandthencacheitoour
//historystorage
(window.addressBookund){
 //Storetheaddressbookasaglobal
 //object.
 //Inarealapplicationwewouldremotely
 //fetchthisfromaserverhe
 //background.
 window.addressBook=
  [\"BradNeuberg’[email protected]’\",
  \"JohnDoe’[email protected]’\",
  \"DeannaNeuberg’[email protected]’\"];

 //cachetheaddressbooksoitexists
 //eventheuserleavesthepageand
 //thenswiththebackbutton
 historyStorage.put(\"addressBook\",addressBook);
}
{
 //fetchthecachedaddressbookfrom
 //thehistorystorage
 window.addressBook=historyStorage.get(\"addressBook\");
}

  处理历史记录变化代码也很简单在下面源代码中无论用户按后退还是前进按钮都将handleHistoryChange使用O’ReillyMail定义displayLocation实用思路方法我们可得到Location并使用它将我们用户界面更新到正确状态


/**Handleshistorychangeevents.*/
functionhandleHistoryChange(Location,
historyData){
 //thereisnolocationthendisplay
 //thedefault,whichistheinbox
 (Location\"\"){
  Location=\"section:inbox\";
 }

 //extractthesectiontodisplayfrom
 //thelocationchange;Locationwill
 //beginwiththeword\"section:\"
 Location=Location.replace(/section\\:/,\"\");

 //updatethebrowsertorespondtothis
 //DHTMLhistorychange
 displayLocation(Location,historyData);
}

/**Displaysthegivenlocationhe
right-handsidecontentarea.*/ [Page]
functiondisplayLocation(Location,sectionData){
 //getthemenuelementthatwasselected
 varselectedElement=document.getElementById(Location);

 //clearouttheoldselectedmenuitem
 varmenu=document.getElementById(\"menu\");
 for(vari=0;i<menu.childNodes.length;i){
  varcurrentElement=menu.childNodes[i];
  //seethisisaDOMElementnode
  (currentElement.nodeType1){
   //clearanyname
   currentElement.Name=\"\";
  }
 }

 //cauheselectedmenuitemto
 //appeardferentlyheUI
 selectedElement.Name=\"selected\";

 //displaythesectionheright-hand
 //sideofthescreen;determihat


 //oursectionDatais

 //displaytheaddressbookdferentlyby
 //usingourlocaladdressdatawecached
 //earlier
 (Location\"addressbook\"){
  //formatanddisplaytheaddressbook
  sectionData=\"<p>Youraddressbook:</p>\";
  sectionData\"<ul>\";

  //fetchtheaddressbookfromthecache
  //wedon’thaveityet
  (window.addressBookund){
   window.addressBook=historyStorage.get(\"addressBook\");
  }

  //formattheaddressbookfordisplay
  for(vari=0;i<window.addressBook.length;i){
   sectionData\"<li>\"
    +window.addressBook[i]
    +\"</li>\";
  }

  sectionData\"</ul>\";
 }

 //IfthereisnosectionData,then
 //remotelyretrieveit;hisexample
 //weusefakedataforeverythingbutthe
 //addressbook
 (sectionDatanull){
  //inarealapplicationwewouldremotely [Page]
  //fetchthissection’scontent
  sectionData=\"<p>Thisissection:\"
+selectedElement.innerHTML+\"</p>\";
 }

 //updatethecontent’stitleandtext
 varcontentTitle=document.getElementById(\"content-title\");
 varcontentValue=document.getElementById(\"content-value\");
 contentTitle.innerHTML=selectedElement.innerHTML;
 contentValue.innerHTML=sectionData;
}
0

相关文章

读者评论

发表评论

  • 昵称:
  • 内容: