﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-Java Votary-随笔分类-Ajax</title><link>http://www.blogjava.net/ericwang/category/5054.html</link><description /><language>zh-cn</language><lastBuildDate>Tue, 27 Feb 2007 13:11:24 GMT</lastBuildDate><pubDate>Tue, 27 Feb 2007 13:11:24 GMT</pubDate><ttl>60</ttl><item><title>【转载】用AJAX来控制书签和回退按钮</title><link>http://www.blogjava.net/ericwang/archive/2005/11/25/21397.html</link><dc:creator>Dion</dc:creator><author>Dion</author><pubDate>Fri, 25 Nov 2005 02:33:00 GMT</pubDate><guid>http://www.blogjava.net/ericwang/archive/2005/11/25/21397.html</guid><wfw:comment>http://www.blogjava.net/ericwang/comments/21397.html</wfw:comment><comments>http://www.blogjava.net/ericwang/archive/2005/11/25/21397.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ericwang/comments/commentRss/21397.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ericwang/services/trackbacks/21397.html</trackback:ping><description><![CDATA[<center>作者：Brad Neuberg</center>
<br>
<center>译者:<a href="http://www.matrix.org.cn/user.shtml?username=boool" target="_new">boool</a></center>
<br>
<br>
<span style="color: Red;">版权声明：任何获得Matrix授权的网站，转载时请<b><u>务必</u></b>以超链接形式标明文章原始出处和作者信息及本声明</span><br>
作者:Brad Neuberg;<a href="http://www.matrix.org.cn/user.shtml?username=boool" target="_new">boool</a><br>
原文地址:<a href="http://www.onjava.com/pub/a/onjava/2005/10/26/ajax-handling-bookmarks-and-back-button.html" target="_new">http://www.onjava.com/pub/a/onjava/2005/10/26/ajax-handling-bookmarks-and-back-button.html</a><br>
中文地址:<a href="http://www.matrix.org.cn/resource/article/43/43972_AJAX.html" target="_new">http://www.matrix.org.cn/resource/article/43/43972_AJAX.html</a><br>
关键词： ajax;bookmarks;back button<br>
<br>
<br>
这篇文章描述了一个支持AJAX应用书签和回退按钮的开源的javascript库。在这个指南的最后，开发者将会得出一个甚至不是<span style="color: Green;">Google Maps </span>或者 <span style="color: Green;">Gmail</span>那样处理的AJAX的解决方案：健壮的，可用的书签和向前向后的动作能够象其他的web页面一样正确的工作。<br>
<br>
<span style="color: Green;"><u>AJAX：怎样去控制书签和回退按钮</u></span> 这篇文章说明了一个重要的成果，AJAX应用目前面对着书签和回退按钮的应用，描述了非常简单的历史库（Really Simple History），一个开源的解决这类问题的框架，并提供了一些能够运行的例子。<br>
<br>
这
篇文章描述的主要问题是双重的，一是一个隐藏的html
表单被用作一个大而短生命周期的客户端信息的session缓存，这个缓存对在这个页面上前进回退是强壮的。二是一个锚连接和隐藏的iframes的组合
用来截取和记录浏览器的历史事件，来实现前进和回退的按钮。这两个技术都被用一个简单的javascript库来封装，以利于开发者的使用。<br>
<br>
<b><span style="font-size: 16px;">存在的问题</span></b><br>
书签和回退按钮在传统的多页面的web应用上能顺利的运行。当用户在网站上冲浪时，他们的浏览器地址栏能更新URL,这些URL可以被粘贴到的email或者添加到书签以备以后的使用。回退和前进按钮也可以正常运行，这可以使用户在他们访问的页面间移动。<br>
<br>
AJAX应用是与众不同的，然而，他也是在单一web页面上成熟的程序。浏览器不是为AJAX而做的—AJAX他捕获过去的事件，当web应用在每个鼠标点击时刷新页面。<br>
<br>
在象Gmail那样的AJAX软件里，浏览器的地址栏正确的停留就象用户在选择和改变应用的状态时，这使得作书签到特定的应用视图里变得不可能。此外，如果用户按下了他们的回退按钮去返回上一个操作，他们会惊奇的发现浏览器将完全离开原来他所在的应用的web页面。<br>
<br>
<b><span style="font-size: 16px;">解决方案</span></b><br>
开
源的Really Simply
History(RSH)框架解决了这些问题，他带来了AJAX应用的作书签和控制前进后退按钮的功能。RSH目前还是beta版，在
Firefox1.0上，Netscape7及以上,和IE6及以上运行。Safari现在还不支持（要得到更详细的说明，请看我的weblog中的文章<a href="http://codinginparadise.org/weblog/2005/09/safari-no-dhtml-history-possible.html" target="_new">Coding in Paradise: Safari: No DHTML History Possible</a>).<br>
<br>
目前存在的几个AJAX框架可以帮助我们做书签和发布历史，然而所有的框架都因为他们的实现而被几个重要的bug困扰（请看<a href="http://codinginparadise.org/weblog/2005/09/ajax-history-libraries.html" target="_new">Coding in Paradise: AJAX History Libraries</a> 得知详情）。此外，许多AJAX历史框架集成绑定到较大的库上，比如Backbase 和 Dojo，这些框架提供了与传统AJAX应用不同的编程模型，强迫开发者去采用一整套全新的方式去获得浏览器的历史相关的功能。<br>
<br>
相应的，RSH是一个简单的模型，能被包含在已经存在的AJAX系统中。而且，Really Simple History库使用了一些技巧去避免影响到其他历史框架的bug.<br>
<br>
Really Simple History框架由2个javascript类库组成，分别叫DhtmlHistory 和 HistoryStorage.<br>
<br>
DhtmlHistory
类提供了一个对AJAX应用提取历史的功能。.AJAX页面add() 历史事件到浏览器里，指定新的地址和关联历史数据。DhtmlHistory
类用一个锚的hash表更新浏览器现在的URL，比如#new-location
，然后用这个新的URL关联历史数据。AJAX应用注册他们自己到历史监听器里，然后当用户用前进和后退按钮导航的时候，历史事件被激发，提供给浏览器新
的地址和调用add()持续保留数据。<br>
<br>
第二个类HistoryStorage，允许开发者存储任意大小的历史数据。一般的页面，当一个用
户导航到一个新的网站，浏览器会卸载和清除所有这个页面的应用和javascript状态信息。如果用户用回退按钮返回过来了，所有的数据已经丢失了。
HistoryStorage 类解决了这个问题，他有一个api
包含简单的hashtable方法比如put(),get(),hasKey()。这些方法允许开发者在离开web页面时存储任意大小的数据，当用户点了
回退按钮返回时，数据可以通过HistoryStorage 类被访问。我们通过一个隐藏的表单域（a hidden form
field），利用浏览器即使在用户离开web页面也会自动保存表单域值的这个特性，完成这个功能。<br>
<br>
让我们立即进入一个简单的例子吧。<br>
<br>
<b>示例1</b><br>
首先，任何一个想使用Really Simple History框架的页面必须包含（include）dhtmlHistory.js 脚本。<br>
<br>
<pre title="pre code" class="overflow">&lt;!-- Load the Really Simple <br>&nbsp;&nbsp;&nbsp;&nbsp; History framework --&gt;<br>&lt;script type="text/javascript"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;src="../../framework/dhtmlHistory.js"&gt;<br>&lt;/script&gt;</pre>
<br>
<br>
DHTML
History 应用也必须在和AJAX web页面相同的目录下包含一个叫blank.html 的指定文件，这个文件被Really Simple
History框架绑定而且对IE来说是必需的。另一方面，RSH使用一个hidden iframe
来追踪和加入IE历史的改变，为了正确的执行功能，这个iframe需要指向一个真正的地址，不需要blank.html。<br>
<br>
RSH框架创建了一个叫dhtmlHistory 的全局对象，作为操作浏览器历史的入口。使用dhtmlHistory 的第一步需要在页面加载后初始化这个对象。<br>
<br>
<pre title="pre code" class="overflow">window.onload = initialize;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>function initialize() {<br>&nbsp;&nbsp;// initialize the DHTML History<br>&nbsp;&nbsp;// framework<br>&nbsp;&nbsp;dhtmlHistory.initialize();</pre>
<br>
<br>
然后，开发者使用dhtmlHistory.addListener()方法去订阅历史改变事件。这个方法获取一个javascript回调方法，当一个DHTML历史改变事件发生时他将收到2个自变量，新的页面地址，和任何可选的而且可以被关联到这个事件的历史数据。<br>
<br>
<pre title="pre code" class="overflow">indow.onload = initialize;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>function initialize() {<br>&nbsp;&nbsp;// initialize the DHTML History<br>&nbsp;&nbsp;// framework<br>&nbsp;&nbsp;dhtmlHistory.initialize();<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;// subscribe to DHTML history change<br>&nbsp;&nbsp;// events<br>&nbsp;&nbsp;dhtmlHistory.addListener(historyChange);</pre>
<br>
<br>
historyChange()方法是简单易懂得，它是由一个用户导航到一个新地址后收到的新地址（newLocation）和一个关联到事件的可选的历史数据historyData 构成的。<br>
<br>
<pre title="pre code" class="overflow">/** Our callback to receive history change<br>&nbsp;&nbsp;&nbsp;&nbsp; events. */<br>function historyChange(newLocation, <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; historyData) {<br>&nbsp;&nbsp;debug("A history change has occurred: "<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+ "newLocation="+newLocation<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+ ", historyData="+historyData, <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;true);<br>}</pre>
<br>
<br>
上面用到的debug()方法是例子代码中定义的一个工具函数，在完整的下载例子里有。debug()方法简单的在web页面上打一条消息，第2个Boolean变量，在代码里是true,控制一个新的debug消息打印前是否要清除以前存在的所有消息。<br>
<br>
一个开发者使用add()方法加入历史事件。加入一个历史事件包括根据历史的改变指定一个新的地址，就像"edit:SomePage"标记, 还提供一个事件发生时可选的会被存储到历史数据historyData值.<br>
<br>
<pre title="pre code" class="overflow">window.onload = initialize;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>function initialize() {<br>&nbsp;&nbsp;// initialize the DHTML History<br>&nbsp;&nbsp;// framework<br>&nbsp;&nbsp;dhtmlHistory.initialize();<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;// subscribe to DHTML history change<br>&nbsp;&nbsp;// events<br>&nbsp;&nbsp;dhtmlHistory.addListener(historyChange);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;// if this is the first time we have<br>&nbsp;&nbsp;// loaded the page...<br>&nbsp;&nbsp;if (dhtmlHistory.isFirstLoad()) {<br>&nbsp;&nbsp;&nbsp;&nbsp;debug("Adding values to browser "<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+ "history", false);<br>&nbsp;&nbsp;&nbsp;&nbsp;// start adding history<br>&nbsp;&nbsp;&nbsp;&nbsp;dhtmlHistory.add("helloworld", <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "Hello World Data");<br>&nbsp;&nbsp;&nbsp;&nbsp;dhtmlHistory.add("foobar", 33);<br>&nbsp;&nbsp;&nbsp;&nbsp;dhtmlHistory.add("boobah", true);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;var complexObject = new Object();<br>&nbsp;&nbsp;&nbsp;&nbsp;complexObject.value1 = <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"This is the first value";<br>&nbsp;&nbsp;&nbsp;&nbsp;complexObject.value2 = <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"This is the second data";<br>&nbsp;&nbsp;&nbsp;&nbsp;complexObject.value3 = new Array();<br>&nbsp;&nbsp;&nbsp;&nbsp;complexObject.value3[0] = "array 1";<br>&nbsp;&nbsp;&nbsp;&nbsp;complexObject.value3[1] = "array 2";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;dhtmlHistory.add("complexObject", <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; complexObject);</pre>
<br>
<br>
在add
()方法被调用后，新地址立刻被作为一个锚值显示在用户的浏览器的URL栏里。例如，一个AJAX
web页面停留在http://codinginparadise.org/my_ajax_app，调用了dhtmlHistory.add
("helloworld", "Hello World Data" 后，用户将在浏览器的URL栏里看到下面的地址<br>
<span style="color: Blue;">http://codinginparadise.org/my_ajax_app#helloworld</span><br>
<br>
然
后他们可以把这个页面做成书签，如果他们使用这个书签，你的AJAX应用可以读出#helloworld值然后使用她去初始化web页面。Hash里的地
址值被Really Simple History&nbsp;&nbsp;框架显式的编码和解码（URL encoded and decoded）
（这是为了解决字符的编码问题）<br>
<br>
对当AJAX地址改变时保存更多的复杂的状态来说，historyData&nbsp;&nbsp;比一个更容易的匹配一个
URL的东西更有用。他是一个可选的值，可以是任何javascript类型，比如Number, String, 或者 Object
类型。有一个例子是用这个在一个多文本编辑器（rich text
editor）保存所有的文本，例如，如果用户从这个页面漂移（或者说从这个页面导航到其他页面，离开了这个页面）走。当一个用户再回到这个地址，浏览器
会把这个对象返回给历史改变侦听器（history change listener）。<br>
<br>
开发者可以提供一个完全的
historyData 的javascript对象，用嵌套的对象objects和排列arrays来描绘复杂的状态。只要是JSON
(JavaScript Object
Notation)&nbsp;&nbsp;允许的那么在历史数据里就是允许的，包括简单数据类型和null型。DOM的对象和可编程的浏览器对象比如
XMLHttpRequest ，不会被保存。注意historyData
不会被书签持久化，如果浏览器关掉，或者浏览器的缓存被清空，或者用户清除历史的时候，会消失掉。<br>
<br>
使用dhtmlHistory
最后一步，是isFirstLoad()
方法。如果你导航到一个web页面，再跳到一个不同的页面，然后按下回退按钮返回起始的网站，第一页将完全重新装载，并激发onload事件。这样能产生
破坏性，当代码在第一次装载时想要用某种方式初始化页面的时候，不会再刷新页面。isFirstLoad()
方法让区别是最开始第一次装载页面，还是相对的，在用户导航回到他自己的浏览器历史中记录的网页时激发load事件，成为可能。<br>
<br>
在例子代码中，我们只想在第一次页面装载的时候加入历史事件，如果用户在第一次装载后，按回退按钮返回页面，我们就不想重新加入任何历史事件。<br>
<br>
<pre title="pre code" class="overflow">window.onload = initialize;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>function initialize() {<br>&nbsp;&nbsp;// initialize the DHTML History<br>&nbsp;&nbsp;// framework<br>&nbsp;&nbsp;dhtmlHistory.initialize();<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;// subscribe to DHTML history change<br>&nbsp;&nbsp;// events<br>&nbsp;&nbsp;dhtmlHistory.addListener(historyChange);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;// if this is the first time we have<br>&nbsp;&nbsp;// loaded the page...<br>&nbsp;&nbsp;if (dhtmlHistory.isFirstLoad()) {<br>&nbsp;&nbsp;&nbsp;&nbsp;debug("Adding values to browser "<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+ "history", false);<br>&nbsp;&nbsp;&nbsp;&nbsp;// start adding history<br>&nbsp;&nbsp;&nbsp;&nbsp;dhtmlHistory.add("helloworld", <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "Hello World Data");<br>&nbsp;&nbsp;&nbsp;&nbsp;dhtmlHistory.add("foobar", 33);<br>&nbsp;&nbsp;&nbsp;&nbsp;dhtmlHistory.add("boobah", true);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;var complexObject = new Object();<br>&nbsp;&nbsp;&nbsp;&nbsp;complexObject.value1 = <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"This is the first value";<br>&nbsp;&nbsp;&nbsp;&nbsp;complexObject.value2 = <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"This is the second data";<br>&nbsp;&nbsp;&nbsp;&nbsp;complexObject.value3 = new Array();<br>&nbsp;&nbsp;&nbsp;&nbsp;complexObject.value3[0] = "array 1";<br>&nbsp;&nbsp;&nbsp;&nbsp;complexObject.value3[1] = "array 2";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;dhtmlHistory.add("complexObject", <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; complexObject);</pre>
<br>
<br>
让
我们继续使用historyStorage 类。类似dhtmlHistory
，historyStorage通过一个叫historyStorage的单一全局对象来显示他的功能，这个对象有几个方法来伪装成一个hash
table, 象put(keyName, keyValue), get(keyName), and
hasKey(keyName).键名必须是字符，同时键值可以是复杂的javascript对象或者甚至是xml格式的字符。在我们源码source
code的例子中，我们put() 简单的XML&nbsp;&nbsp;到historyStorage 在页面第一次装载时。<br>
<br>
<pre title="pre code" class="overflow">window.onload = initialize;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>function initialize() {<br>&nbsp;&nbsp;// initialize the DHTML History<br>&nbsp;&nbsp;// framework<br>&nbsp;&nbsp;dhtmlHistory.initialize();<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;// subscribe to DHTML history change<br>&nbsp;&nbsp;// events<br>&nbsp;&nbsp;dhtmlHistory.addListener(historyChange);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;// if this is the first time we have<br>&nbsp;&nbsp;// loaded the page...<br>&nbsp;&nbsp;if (dhtmlHistory.isFirstLoad()) {<br>&nbsp;&nbsp;&nbsp;&nbsp;debug("Adding values to browser "<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+ "history", false);<br>&nbsp;&nbsp;&nbsp;&nbsp;// start adding history<br>&nbsp;&nbsp;&nbsp;&nbsp;dhtmlHistory.add("helloworld", <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "Hello World Data");<br>&nbsp;&nbsp;&nbsp;&nbsp;dhtmlHistory.add("foobar", 33);<br>&nbsp;&nbsp;&nbsp;&nbsp;dhtmlHistory.add("boobah", true);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;var complexObject = new Object();<br>&nbsp;&nbsp;&nbsp;&nbsp;complexObject.value1 = <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"This is the first value";<br>&nbsp;&nbsp;&nbsp;&nbsp;complexObject.value2 = <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"This is the second data";<br>&nbsp;&nbsp;&nbsp;&nbsp;complexObject.value3 = new Array();<br>&nbsp;&nbsp;&nbsp;&nbsp;complexObject.value3[0] = "array 1";<br>&nbsp;&nbsp;&nbsp;&nbsp;complexObject.value3[1] = "array 2";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;dhtmlHistory.add("complexObject", <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; complexObject);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;// cache some values in the history<br>&nbsp;&nbsp;&nbsp;&nbsp;// storage<br>&nbsp;&nbsp;&nbsp;&nbsp;debug("Storing key 'fakeXML' into " <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+ "history storage", false);<br>&nbsp;&nbsp;&nbsp;&nbsp;var fakeXML = <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'&lt;?xml version="1.0" '<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'encoding="ISO-8859-1"?&gt;'<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'&lt;foobar&gt;'<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; '&lt;foo-entry/&gt;'<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'&lt;/foobar&gt;';<br>&nbsp;&nbsp;&nbsp;&nbsp;historyStorage.put("fakeXML", fakeXML);<br>&nbsp;&nbsp;} </pre>
<br>
<br>
然后，如果用户从这个页面漂移走（导航走）又通过返回按钮返回了，我们可以用get（）提出我们存储的值或者用haskey（）检查他是否存在。<br>
<br>
<pre title="pre code" class="overflow">window.onload = initialize;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>function initialize() {<br>&nbsp;&nbsp;// initialize the DHTML History<br>&nbsp;&nbsp;// framework<br>&nbsp;&nbsp;dhtmlHistory.initialize();<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;// subscribe to DHTML history change<br>&nbsp;&nbsp;// events<br>&nbsp;&nbsp;dhtmlHistory.addListener(historyChange);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;// if this is the first time we have<br>&nbsp;&nbsp;// loaded the page...<br>&nbsp;&nbsp;if (dhtmlHistory.isFirstLoad()) {<br>&nbsp;&nbsp;&nbsp;&nbsp;debug("Adding values to browser "<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+ "history", false);<br>&nbsp;&nbsp;&nbsp;&nbsp;// start adding history<br>&nbsp;&nbsp;&nbsp;&nbsp;dhtmlHistory.add("helloworld", <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "Hello World Data");<br>&nbsp;&nbsp;&nbsp;&nbsp;dhtmlHistory.add("foobar", 33);<br>&nbsp;&nbsp;&nbsp;&nbsp;dhtmlHistory.add("boobah", true);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;var complexObject = new Object();<br>&nbsp;&nbsp;&nbsp;&nbsp;complexObject.value1 = <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"This is the first value";<br>&nbsp;&nbsp;&nbsp;&nbsp;complexObject.value2 = <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"This is the second data";<br>&nbsp;&nbsp;&nbsp;&nbsp;complexObject.value3 = new Array();<br>&nbsp;&nbsp;&nbsp;&nbsp;complexObject.value3[0] = "array 1";<br>&nbsp;&nbsp;&nbsp;&nbsp;complexObject.value3[1] = "array 2";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;dhtmlHistory.add("complexObject", <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; complexObject);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;// cache some values in the history<br>&nbsp;&nbsp;&nbsp;&nbsp;// storage<br>&nbsp;&nbsp;&nbsp;&nbsp;debug("Storing key 'fakeXML' into " <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+ "history storage", false);<br>&nbsp;&nbsp;&nbsp;&nbsp;var fakeXML = <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'&lt;?xml version="1.0" '<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'encoding="ISO-8859-1"?&gt;'<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'&lt;foobar&gt;'<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; '&lt;foo-entry/&gt;'<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'&lt;/foobar&gt;';<br>&nbsp;&nbsp;&nbsp;&nbsp;historyStorage.put("fakeXML", fakeXML);<br>&nbsp;&nbsp;} <br>&nbsp;&nbsp;<br>&nbsp;&nbsp;// retrieve our values from the history<br>&nbsp;&nbsp;// storage<br>&nbsp;&nbsp;var savedXML = <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;historyStorage.get("fakeXML");<br>&nbsp;&nbsp;savedXML = prettyPrintXml(savedXML);<br>&nbsp;&nbsp;var hasKey = <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; historyStorage.hasKey("fakeXML");<br>&nbsp;&nbsp;var message =<br>&nbsp;&nbsp;&nbsp;&nbsp;"historyStorage.hasKey('fakeXML')="<br>&nbsp;&nbsp;&nbsp;&nbsp;+ hasKey + "&lt;br&gt;"<br>&nbsp;&nbsp;&nbsp;&nbsp;+ "historyStorage.get('fakeXML')=&lt;br&gt;"<br>&nbsp;&nbsp;&nbsp;&nbsp;+ savedXML;<br>&nbsp;&nbsp;debug(message, false);<br>}</pre>
<br>
<br>
prettyPrintXml() 是一个第一在例子源码full example source code中的工具方法。这个方法准备简单的xml显示在web page ，方便调试。<br>
<br>
注
意数据只是在使用页面的历史时被持久化，如果浏览器关闭了，或者用户打开一个新的窗口又再次键入了ajax应用的地址，历史数据对这些新的web页面是不
可用的。历史数据只有在用前进或回退按钮时才被持久化，而且在用户关闭浏览器或清空缓存的时候会消失掉。想真正的长时间的持久化，请看Ajax
MAssive Storage System (AMASS).<br>
我们的简单示例已经完成。演示他（Demo it）或者下载全部的源代码（download the full source code.）<br>
<br>
<b>示例2</b><br>
我
们的第2个例子是一个简单的模拟ajax email&nbsp;&nbsp;应用的示例，叫O'Reilly Mail,类似Gmail. O'Reilly
Mail描述了怎样使用dhtmlHistory类去控制浏览器的历史，和怎样使用historyStorage对象去缓存历史数据。<br>
<br>
O'Reilly
Mail 用户接口（user interface）有两部分。在页面的左边是一个有不同email文件夹和选项的菜单，例如
收件箱，草稿，等等。当一个用户选择了一个菜单项，比如收件箱，我们用这个菜单项的内容更新右边的页面。在一个实际应用中，我们会远程取得和显示选择的信
箱内容，不过在O'Reilly Mail里，我们简单的显示选择的选项。<br>
<br>
O'Reilly Mail使用Really Simple History 框架向浏览器历史里加入菜单变化和更新地址栏，允许用户利用浏览器的回退和前进按钮对应用做书签和跳到上一个变化的菜单。<br>
<br>
我
们加入一个特别的菜单项，地址簿，来描绘historyStorage
能够怎样被使用。地址簿是一个由联系的名字电子邮件和地址组成的javascript数组，在一个真实的应用里我们会取得他从一个远程的服务器。不过，在
O'Reilly Mail里，我们在本地创建这个数组，加入几个名字电子邮件和地址，然后把他们存储在historyStorage
对象里。如果用户离开了这个web页面以后又返回的话，O'Reilly Mail应用重新从缓存里得到地址簿，胜过（不得不）再次访问远程服务器。<br>
<br>
地址簿是在我们的初始化initialize()方法里存储和重新取得的<br>
<br>
<pre title="pre code" class="overflow">/** Our function that initializes when the page<br>&nbsp;&nbsp;&nbsp;&nbsp;is finished loading. */<br>function initialize() {<br>&nbsp;&nbsp; // initialize the DHTML History framework<br>&nbsp;&nbsp; dhtmlHistory.initialize();<br>&nbsp;&nbsp; <br>&nbsp;&nbsp; // add ourselves as a DHTML History listener<br>&nbsp;&nbsp; dhtmlHistory.addListener(handleHistoryChange);<br><br>&nbsp;&nbsp; // if we haven't retrieved the address book<br>&nbsp;&nbsp; // yet, grab it and then cache it into our<br>&nbsp;&nbsp; // history storage<br>&nbsp;&nbsp; if (window.addressBook == undefined) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Store the address book as a global<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// object.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// In a real application we would remotely<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// fetch this from a server in the<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// background.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;window.addressBook =<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ["Brad Neuberg 'bkn3@columbia.edu'",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"John Doe 'johndoe@example.com'",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Deanna Neuberg 'mom@mom.com'"];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// cache the address book so it exists<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// even if the user leaves the page and<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// then returns with the back button<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;historyStorage.put("addressBook",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; addressBook);<br>&nbsp;&nbsp; }<br>&nbsp;&nbsp; else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// fetch the cached address book from<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// the history storage<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;window.addressBook = <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; historyStorage.get("addressBook");<br>&nbsp;&nbsp; }</pre>
<br>
<br>
处
理历史变化的代码是简单的。在下面的代码中，当用户不论按下回退还是前进按钮handleHistoryChange
都被调用。我们得到新的地址（newLocation）
使用他更新我们的用户接口来改变状态，通过使用一个叫displayLocation的O'Reilly Mail的工具方法。<br>
<br>
<pre title="pre code" class="overflow">/** Handles history change events. */<br>function handleHistoryChange(newLocation, <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; historyData) {<br>&nbsp;&nbsp; // if there is no location then display<br>&nbsp;&nbsp; // the default, which is the inbox<br>&nbsp;&nbsp; if (newLocation == "") {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;newLocation = "section:inbox";<br>&nbsp;&nbsp; }<br>&nbsp;&nbsp; <br>&nbsp;&nbsp; // extract the section to display from<br>&nbsp;&nbsp; // the location change; newLocation will<br>&nbsp;&nbsp; // begin with the word "section:" <br>&nbsp;&nbsp; newLocation = <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; newLocation.replace(/section\:/, "");<br>&nbsp;&nbsp; <br>&nbsp;&nbsp; // update the browser to respond to this<br>&nbsp;&nbsp; // DHTML history change<br>&nbsp;&nbsp; displayLocation(newLocation, historyData);<br>}<br><br>/** Displays the given location in the <br>&nbsp;&nbsp;&nbsp;&nbsp;right-hand side content area. */<br>function displayLocation(newLocation,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sectionData) {<br>&nbsp;&nbsp; // get the menu element that was selected<br>&nbsp;&nbsp; var selectedElement = <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;document.getElementById(newLocation);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp; // clear out the old selected menu item<br>&nbsp;&nbsp; var menu = document.getElementById("menu");<br>&nbsp;&nbsp; for (var i = 0; i &lt; menu.childNodes.length;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;i++) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var currentElement = menu.childNodes[i];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// see if this is a DOM Element node<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (currentElement.nodeType == 1) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // clear any class name<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; currentElement.className = "";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp; } <br>&nbsp;&nbsp; <br>&nbsp;&nbsp; // cause the new selected menu item to<br>&nbsp;&nbsp; // appear differently in the UI<br>&nbsp;&nbsp; selectedElement.className = "selected";<br>&nbsp;&nbsp; <br>&nbsp;&nbsp; // display the new section in the right-hand<br>&nbsp;&nbsp; // side of the screen; determine what <br>&nbsp;&nbsp; // our sectionData is<br>&nbsp;&nbsp; <br>&nbsp;&nbsp; // display the address book differently by<br>&nbsp;&nbsp; // using our local address data we cached<br>&nbsp;&nbsp; // earlier<br>&nbsp;&nbsp; if (newLocation == "addressbook") {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// format and display the address book<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sectionData = "&lt;p&gt;Your addressbook:&lt;/p&gt;";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sectionData += "&lt;ul&gt;";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// fetch the address book from the cache<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// if we don't have it yet<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (window.addressBook == undefined) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; window.addressBook = <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; historyStorage.get("addressBook");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// format the address book for display<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (var i = 0; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i &lt; window.addressBook.length;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i++) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sectionData += "&lt;li&gt;"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+ window.addressBook[i]<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+ "&lt;/li&gt;";&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sectionData += "&lt;/ul&gt;";<br>&nbsp;&nbsp; }<br>&nbsp;&nbsp; <br>&nbsp;&nbsp; // If there is no sectionData, then <br>&nbsp;&nbsp; // remotely retrieve it; in this example<br>&nbsp;&nbsp; // we use fake data for everything but the<br>&nbsp;&nbsp; // address book<br>&nbsp;&nbsp; if (sectionData == null) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// in a real application we would remotely<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// fetch this section's content<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sectionData = "&lt;p&gt;This is section: " <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + selectedElement.innerHTML + "&lt;/p&gt;";&nbsp;&nbsp;<br>&nbsp;&nbsp; }<br>&nbsp;&nbsp; <br>&nbsp;&nbsp; // update the content's title and main text<br>&nbsp;&nbsp; var contentTitle = <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; document.getElementById("content-title");<br>&nbsp;&nbsp; var contentValue =<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; document.getElementById("content-value");<br>&nbsp;&nbsp; contentTitle.innerHTML = <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;selectedElement.innerHTML;<br>&nbsp;&nbsp; contentValue.innerHTML = sectionData;<br>}</pre>
<br>
<br>
演示（Demo）O'Reilly Mail或者下载（download）O'Reilly Mail的源代码。<br>
<br>
<b><span style="font-size: 16px;">结束语</span></b><br>
你现在已经学习了使用Really Simple History API 让你的AJAX应用响应书签和前进回退按钮，而且有代码可以作为创建你自己的应用的素材。我热切地期待你利用书签和历史的支持完成你的AJAX创造。<br>
<br>
资源<br>
·onjava.com:<a href="http://www.onjava.com/" target="_new">onjava.com</a><br>
·Matrix-Java开发者社区:<a href="http://www.matrix.org.cn/" target="_new">http://www.matrix.org.cn/</a><br>
·<a href="http://www.onjava.com/onjava/2005/10/26/examples/downloads/examples.zip" target="_new">Download all sample code for this article. </a><br>
·<a href="http://codinginparadise.org/projects/dhtml_history/latest.zip" target="_new">Download the Really Simple History framework. </a><br>
·Demo O'Reilly Mail or <a href="http://www.onjava.com/onjava/2005/10/26/examples/downloads/examples.zip" target="_new">download</a> the O'Reilly Mail source code. The full example download also includes more examples for you to play with. <br>
·<a href="http://codinginparadise.org/" target="_new">Coding in Paradise</a>:
The author's weblog, covering AJAX, DHTML, and Java techniques and new
developments in collaborative technologies, such as WikiWikis. <br>
<br>
<br>
<b>感谢</b><br>
特别的要感谢每个检阅这篇文章的the Really Simple History框架的人：<br>
Michael
Eakes, Jeremy Sevareid, David Barrett, Brendon Wilson, Dylan Parker,
Erik Arvidsson, Alex Russell, Adam Fisk, Alex Lynch, Joseph Hoang Do,
Richard MacManus, Garret Wilson, Ray Baxter, Chris Messina, and David
Weekly.<br>
<img src ="http://www.blogjava.net/ericwang/aggbug/21397.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ericwang/" target="_blank">Dion</a> 2005-11-25 10:33 <a href="http://www.blogjava.net/ericwang/archive/2005/11/25/21397.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个用于J2EE应用程序的Backbase Ajax前端</title><link>http://www.blogjava.net/ericwang/archive/2005/11/23/21178.html</link><dc:creator>Dion</dc:creator><author>Dion</author><pubDate>Wed, 23 Nov 2005 12:31:00 GMT</pubDate><guid>http://www.blogjava.net/ericwang/archive/2005/11/23/21178.html</guid><wfw:comment>http://www.blogjava.net/ericwang/comments/21178.html</wfw:comment><comments>http://www.blogjava.net/ericwang/archive/2005/11/23/21178.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ericwang/comments/commentRss/21178.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ericwang/services/trackbacks/21178.html</trackback:ping><description><![CDATA[<span class="h1b">一个用于J2EE应用程序的Backbase Ajax前端</span><br>


<br>

<table border="0" cellpadding="0" cellspacing="0" width="100%">

  <tbody><tr>
    <td height="64">时间：2005-11-03<br>
作者：<a href="http://dev2dev.bea.com.cn/author/404.html">Mark Schiefelbein</a><br>
浏览次数：
<script language="JavaScript" type="text/JavaScript" src="http://203.81.25.103/cgi-bin/beadevcount.cgi?d_id=682"></script>628
<br>
本文关键字：<a href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords=Ajax">Ajax</a>,&nbsp;<a href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords=RIA">RIA</a>,&nbsp;<a href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords=Rich%20Internet%20Application">Rich Internet Application</a>,&nbsp;<a href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords=backbase">backbase</a>,&nbsp;<a href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords=Dev%20Toolbox">Dev Toolbox</a>,&nbsp;<a href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords=%20Eclipse"> Eclipse</a>,&nbsp;<a href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords=%20WebLogic"> WebLogic</a></td>
    <td><table class="box_content" border="0" cellpadding="0" cellspacing="0">
      <tbody><tr>
        <td><span class="h2b">文章工具</span><br>
            <img src="http://dev2dev.bea.com.cn/images/letter001.gif" alt="推荐给朋友" align="middle" height="10" width="19">&nbsp;<a href="javascript:sendmail()">推荐给朋友</a><br>
            <img src="http://dev2dev.bea.com.cn/images/print001.gif" alt="打印文章" align="middle" height="18" width="19">&nbsp;<a href="javascript:window.print()">打印文章</a></td>
      </tr>
    </tbody></table></td>
  </tr>
</tbody>
</table>


<!-- 提取技术文章 -->
<div class="beas"><img src="http://dev2dev.bea.com.cn/images/dot6B6B6B.gif" alt="" height="1" width="100%"></div>


<p>　　动态HTML技术已经出现了多年。最近，Google的最新Web应用程序GMail、Google Suggests和Google
Maps，在前端页面中重新引入了基于标准的DHTML开发模型。Google证明了，DHTML开发模型能够让开发人员创建具有可视化吸引力和高度交互
式的Rich Internet Application（丰富网络应用程序，RIA）。</p>

<p>　　Adaptive Path公司的Jesse James Garrett为这个基于标准的RIA开发模型创造了术语<a href="http://www.adaptivepath.com/publications/essays/archives/000385.php" target="_blank">Ajax (Asynchronous JavaScript + XML)</a>。与传统的基于页面的Web应用程序模型相比，Ajax有3点不同之处：</p>

<ul>
<li>有一个客户端引擎担任用户界面（UI）和服务器之间的中介。</li><li>用户行为由客户端引擎处理，而不是生成发往服务器的页面请求。</li><li>XML数据在客户端引擎和服务器之间传输。</li>
</ul>

<p>　　换言之，Ajax解决方案包括一个客户端引擎，它用于呈现用户界面，并使用XML格式与服务器通信。这个引擎由很多JavaScript函数组成，位于Web浏览器中，它不需要插件，也不需要用户安装。</p>

<p>　　基于Ajax的RIA正在迅速成为Web应用程序前端的基准，因为它可以同时提供二者的优点：丰富性和可达性。Ajax应用程序和桌面应用程序
一样丰富，响应高度灵敏，并且可以在一个页面上提供所有数据，无需刷新页面。它们还拥有基于标准的浏览器应用程序的可达性特点，这类应用程序可以在不具备
浏览器插件或客户端applet的情况下进行部署。</p>

<p>　　Backbase所提供的Ajax软件具有以下特点：基于标准、功能全面且易于使用。Backbase Presentation
Client (BPC)基于Ajax技术，它使用称为Backbase XML (BXML)的附加标签扩展了DHTML。Backbase XML
Server Edition for J2EE
(BXS)包含了一些服务器端的组件，利用这些组件，J2EE开发人员可以快速开发J2EE应用程序的Ajax前端。</p>

<p>　　在本文中，我使用Backbase为Java Pet Store开发了一个基于Ajax的前端。该案例分析说明了如何使用Backbase技术作为J2EE应用程序的Ajax表示层。您可以查看文中所描述的应用程序的在线演示，网址是<a href="http://www.backbase.com/xmlserver" target="_blank">http://www.backbase.com/xmlserver</a>。</p>

<p><strong>Backbase Ajax表示层</strong></p>

<p>　　Web开发人员应该能够轻松创建具有以下特点的Rich Internet Application
(RIA)：完全基于HTML标准（W3C），不需要最终用户安装插件，速度超快，能够在所有浏览器上进行操作，并与J2EE运行时和开发环境完全集成。
RIA利用客户端（Web浏览器）资源创建和管理用户界面，从而为最终用户提供一个响应灵敏而且具有应用程序风格的用户界面。</p>

<p>　　这种方法最近被称为Ajax。Ajax这个术语的灵感来源于Gmail、Google Maps和Google
Suggests这类应用程序，它把现有的浏览器技术提高到了一个新的水平上。RIA从根本上改进了在线应用程序的可用性和有效性。Ajax
RIA只使用标准的浏览器技术（如JavaScript、XHTML和XMLHttpRequest对象）就做到了这一点。通过使用
XMLHttpRequest，在将数据异步加载到界面中时就无需刷新页面。</p>

<p>　　Backbase在J2EE架构中提供一个Ajax表示层，它结合了目前的J2EE服务器和先进的富客户端技术的优点。Backbase表示层
控制了富用户界面的每个方面：与最终用户的交互模型，与后端系统的集成，以及整个客户端-服务器通信。Backbase直接提供了用于聚合来自任意位置的
XML的下一个范型，将数据绑定到先进的富用户界面控件，并在一个统一的富用户界面中交付组合应用程序。</p>

<p>　　Backbase表示层由一个客户机和一个服务器组成。Backbase Presentation Client
(BPC)是一个基于Ajax的GUI引擎，它允许开发人员以声明性的方式快速构建RIA。Backbase
XML(BXML)是对XHTML的扩展。它为开发人员提供了交付富前端功能的附加标签(B tag)。Backbase XML Server
(BXS)提供一种XML流水线架构，利用它可以从Web服务、数据库或Java对象获取数据，可以聚合和转换这些数据，并将其绑定到BPC中的UI元
素。BPC和BXS相结合，可以在Web浏览器和应用服务器之间搭建一座功能强大的桥梁，并提供一个分布在客户端和服务器上的完整的富Internet表
示层。</p>

<p>　　图1说明了在逻辑和物理应用程序架构中，Backbase所处的位置。应用程序由一个J2EE后端和一个基于Ajax的RIA前端组成。从逻辑
上说，Backbase提供了表示层，而J2EE提供了业务逻辑和数据层。从物理上说，表示层分布在客户端和服务器上。在客户端上，Backbase使用
BPC扩展了浏览器。在服务器上，Backbase使用BXS扩展了应用服务器。</p>

<p> <img src="http://dev2dev.bea.com.cn/images/051102/0511020101.jpg" height="235" width="301"> </p>

<p>图1. Backbase富Internet表示层</p>

<p><strong>Pet Store案例分析</strong></p>

<p>　　我们将使用Java Pet Store作为案例来分析如何为J2EE应用程序添加Backbase RIA前端。Java Pet
Store Demo是Sun Microsystems提供的一个示例应用程序，其目的是为了演示如何使用Java 2 Platform,
Enterprise Edition(J2EE)构建Web应用程序（详情请参见<a href="http://java.sun.com/developer/releases/petstore" target="_blank">http://java.sun.com/developer/releases/petstore</a>）。</p>

<p>　　Java Pet Store是业内一个著名的参考应用程序（pet store还有.NET和Flash版本）。由于以下两个原因，它成为为J2EE应用程序添加基于Ajax的RIA前端的完美案例：</p>

<ul>
<li>Java Pet Store是一个完整的Web应用程序。</li><p>Sun设计Pet Store的目的是演示所有常见的Web应用程序功能。通过使用Pet Store作为案例，我可以说明为J2EE应用程序添加RIA层的所有方面。</p><p>作为一个典型的在线商店，它包含以下功能：</p><ul><li>浏览产品类别。</li><li>在购物车中添加和删除物品。</li><li>填写订单表单。</li><li>提交订单。</li></ul><li>Java Pet Store有一个传统的HTML前端。</li><p>使用RIA前端的目的是提供更简单和响应更灵敏的GUI，以及通常更为丰富的Web用户体验。我将说明，如何通过Backbase RIA技术极大地改进应用程序的前端，同时无需对后端和总体系统需求做任何修改。</p><p>Pet Store的RIA前端将通过以下方式改善可用性：</p><li>把前端变为一个单页面的界面（SPI）。</li><li>提供更先进的UI控件（如模态弹出式菜单）。</li><li>使用可视化效果（例如，把宠物放入购物车）。</li><li>更加有效地利用电脑屏幕的操作区域。</li>
</ul>


<p><strong>RIA Pet Store前端</strong></p>

<p>　　在这一节中，我将讨论经过改进的新Pet Store RIA前端。</p>

<p>　　下面的两个屏幕快照演示了前端的改进。要获得对Backbase RIA前端更直观的感受，请访问<a href="http://www.backbase.com/xmlserver" target="_blank">http://www.backbase.com/xmlserver</a>上的在线演示，或者到<a href="http://www.backbase.com/download" target="_blank">http://www.backbase.com/download</a>下载Backbase社区版本。</p>

<p>下面两个图对两个前端进行了可视化的比较。图2显示的是原来静态的多页面HTML前端。图3显示的是新的Backbase SPI前端：
</p>

<p> <img src="http://dev2dev.bea.com.cn/images/051102/0511020102.jpg" height="186" width="325"> </p>

<p> 图2. 原始HTML前端 </p>

<p> <img src="http://dev2dev.bea.com.cn/images/051102/0511020103.jpg" height="232" width="325"> </p>

<p>图3. 新Backbase前端</p>

<p>　　Backbase为创建丰富的单页面Web界面提供了许多可能性。下面列出了一些Pet Store所使用的例子。</p>

<ul>
<li>选项卡式的单页面浏览</li><p>在Web界面上，不同的动物种类（狗、猫等等）被表示为不同的选项卡。点击一个选项卡就会打开相应的类别，显示可供出售的宠物。</p><p>在Backbase SPI中，无需刷新页面就可以打开选项卡。BPC只从服务器请求所需的数据，然后更新客户端的视图。SPI机制可以极大地缩短响应时间，让客户随心所欲地在类别之间来回穿梭。</p><li>活动的多功能界面</li><p>界面有三个主要功能——类别浏览、购物车和页面引导历史记录，它们在界面上都是一直可见的。因此，购物者总是能够查看购物车的当前内容或最近看过的宠物的记录。</p><p>这些功能是高度同步的：浏览一个宠物时，历史记录将自动更新为在记录中显示该宠物。定购一个宠物时，它将被添加到购物车中。上述一切都发生在客户端的一个页面上（例如，无需重新加载页面就可以更新界面的各个部分）。</p><li>界面变化的流畅可视化效果</li><p>进行浏览时，客户将会看到不断变化的界面视图。例如，他可以按照价格和名称对宠物进行排序。界面需要根据新的排列顺序显示更新以后的宠物清单。</p><p>在Backbase RIA前端中，以前的视图被使用可视化效果的新视图所代替，新视图向最终用户显示什么正在改变。图4说明了如何通过流畅的定位效果，把按名称排列的顺序转变为按价格排列的顺序：
</p><p> <img src="http://dev2dev.bea.com.cn/images/051102/0511020104.jpg" height="56" width="313"> </p><p>图4.类别视图的排列顺序转换</p><li>用于提高转换速度的信息栏验证</li>
</ul>

<p>　　为了执行购买，购买者必须在一份表单中填入个人详细信息。Backbase极大地简化了这个购买过程，通过客户端的信息栏验证提供即时的反馈，并在提供所有数据的过程中提供逐步的指南和概述。</p>

<p>　　图5显示了在填写表单的第一个步骤中，对于e-mail地址信息栏的验证。当购买者填写下一栏时，就会提供即时的反馈。
</p>

<p> <img src="http://dev2dev.bea.com.cn/images/051102/0511020105.jpg" height="135" width="325"> </p>

<p>图5. 信息栏验证—e-mail栏</p>

<p><strong>Backbase RIA Pet Store的架构</strong></p>

<p>　　增强Pet Store（或其他任何Web应用程序）的前端时，我们将继续依赖于以下两条架构基本原则：</p>

<ul>
<li>最终用户仍然使用标准的Web浏览器访问Pet Store，无需添加任何插件。</li><li>由J2EE业务逻辑和数据组成的整个后端保持不变。</li>
</ul>

<p>　　现有的后端在开发期间是完全孤立的，而且不会改变，这个事实对于架构师和IT管理人员十分有利。通过一个规整的、模块化的架构，他们将能够控制风险和成本，同时显著提高Web应用程序的用户友好性。</p>

<p>　　Backbase的富表示层技术由两个模块组成，它们将被加入到架构中。在客户端，BPC管理着SPI，并通过异步响应事件来处理与最终用户之
间的交互。在服务器端，Backbase XML
Server这个灵活的XML管道可以连接到任意服务器端的数据源，包括Web服务、文件、数据库或本地Java对象。图6说明了BPC和BXS如何共同
为RIA提供一个声明式的、基于XML的端到端表示层。
</p>

<p> <img src="http://dev2dev.bea.com.cn/images/051102/0511020106.jpg" height="149" width="299"> </p>

<p>图6. 声明式的端到端表示层</p>

<p><strong>Backbase表示客户端</strong></p>

<p>　　BPC是一个基于Ajax的GUI引擎，它运行在标准的Web浏览器中。运行时，BPC被加载到浏览器中，然后它会接收BXML代码，构造对应的B树，并不断地把这种表示转换为浏览器所呈现的DOM树。图7说明了运行时转换过程。
</p>

<p> <img src="http://dev2dev.bea.com.cn/images/051102/0511020107.jpg" height="212" width="296"> </p>

<p>图7. BPC运行时</p>

<p><strong>Backbase XML</strong></p>

<p>　　Backbase XML (BXML)是XHTML的扩展。开发人员通过创建BXML应用程序来开发富前端，包括BXML标签、标准的XHTML和CSS。BXML是一种声明性语言，它包含了XHTML中所没有的标签（B标签）</p>

<p>　　BXML包含用于下列用途的标签：</p>

<ul>
<li>定义屏幕分区(&lt;b:panel&gt;) </li><li>交互式客户端控制(&lt;b:menu&gt;) </li><li>处理标准的用户交互事件(onClick) </li><li>处理高级的用户交互事件(拖放和调整大小) </li><li>管理客户端状态</li><li>处理可视化效果(使修改任意CSS属性的过程动画化) </li><li>数据绑定</li><li>使用XSLT的一个子集进行客户端转换</li>
</ul>

<p><strong>用于J2EE的Backbase XML Server</strong></p>

<p>　　Backbase XML Server (BXS)是一个服务器端的引擎，用于把BPC链接到任意J2EE后端。和BPC一样，BXS是完全基于XML的，其编程是声明性的。它使用一种XML管道架构，提供功能强大的服务器端转换和聚合。</p>

<p>　　BXS附带一些用于访问最常用的数据源（包括Web服务、数据库、文件系统和本地Java对象）的开箱即用任务。我们使用Backbase标签对从这些源获得的数据进行聚合，然后使用XSLT进行转换。结果以无格式XML数据或BXML表示代码的形式返回给BPC。</p>

<p>　　BXS还提供一些应用服务，包括身份验证、授权、日志记录和用户跟踪。图8显示了BXS的总体架构。</p>

<p> <img src="http://dev2dev.bea.com.cn/images/051102/0511020108.jpg" height="217" width="302"> </p>

<p>图8. BXS架构</p>

<p><strong>Eclipse开发工具</strong></p>

<p>　　为了让J2EE开发人员可以只使用一种开发工具就能创建完整的Web应用程序，包括富前端，Backbase提供了一个Eclipse插件。如图9所示，该插件提供了在Eclipse中突出显示语法和Backbase标签代码自动完成的功能。
</p>

<p> <img src="http://dev2dev.bea.com.cn/images/051102/0511020109.jpg" height="235" width="317"> </p>

<p>图9. Backbase Eclipse插件</p>

<p>　　注意：Eclipse的可视化拖放开发插件还处在开发阶段。</p>

<p><strong>部署到BEA WebLogic</strong></p>

<p>　　BXS是一个与标准兼容的J2EE应用程序，可以将其部署到任何J2EE应用服务器上。图10显示了如何使用WebLogic控制台把BXS部署到<a href="http://dev2dev.bea.com/wlserver/" target="_blank">BEA WebLogic Server</a>。
</p>

<p> <img src="http://dev2dev.bea.com.cn/images/051102/0511020111.jpg" height="251" width="325"> </p>

<p>图10. 把BXS部署到BEA WebLogic</p>

<p><strong>实现Backbase RIA Pet Store</strong></p>

<p>　　下面的顺序图包括更多详细信息，可以帮助您更好地理解如何实现Backbase pet store。该顺序图显示了在应用程序的初始化加载期间BPC与BXS之间的交互，如图11所示，它包括以下4个步骤：</p>

<ul>
<li>初始化：用户在浏览器中输入宠物商店的URL；对BPC进行初始化。</li><li>应用程序布局：触发正在构造的事件；BPC构建整体应用程序布局；宠物类别被加载并显示在选项卡中。</li><li>默认数据：默认情况下加载狗的类别；最初显示8张狗的图片，并带有向前/向后和排序功能。</li>
</ul>

<p>　　用户交互：用户点击Next按钮便可显示编号从9到16的狗图片。
</p>

<p> <img src="http://dev2dev.bea.com.cn/images/051102/0511020110.jpg" height="365" width="329"> </p>

<p>图11.顺序图：富商店前端</p>

<ul>
<li>初始化</li><p>从用户在浏览器中输入宠物商店的URL开始，这将导致从Web服务器请求一个索引页面。</p><p>索引页面包含用于实例化BPC的代码。索引页面是XHTML和BXML标签的结合，包含负责启动富前端的初始化事件处理程序。</p><p>BPC初始化代码：</p><pre class="code">&lt;...&gt;&lt;body onload="bpc.boot('/Backbase/')"&gt;<br><br>&lt;...&gt;<br><br>  &lt;xmp b:backbase="true"<br><br>          style="display:none;height:100%;"&gt;<br><br>    &lt;s:loading&gt;<br><br>      &lt;div style="position:absolute;width:20%;<br><br>                     top: 50px;left: 35%;"&gt;<br><br>        &lt;center&gt;Please wait while loading...<br><br>        &lt;/center&gt;<br><br>      &lt;/div&gt;<br><br>    &lt;/s:loading&gt;<br><br>    &lt;...&gt;<br><br>    &lt;!-- Include petshop specific behaviors --&gt;<br><br>    &lt;s:include b:url="petshop.xml"/&gt;<br></pre><li>应用程序布局</li><p>加载页面之后，BPC就会处理正在构造的事件，以便开始构建总体的应用程序布局。</p><p>应用程序布局由几个面板组成，它们将屏幕划分为几个部分。顶行有一个固定高度的宠物商店徽标，接下来的主行是实际的商店，大小可以调整。主行分为两列，左边一列是产品类别，右边一列是购物车和历史记录。</p>
产品类别使用选项卡式的导航，每个宠物类别一个选项卡。这些选项卡是动态构造的，具体过程是通过BXS从一个XML文件加载类别，然后通过一个客户端模板把这些类别转换为选项卡，该转换模板的BPC代码如下：
<pre class="code">&lt;s:task b:action="transform"<br><br>    b:stylesheet="b:xml('categories')"<br><br>    b:xmldatasource="b:url('categories.xml')"<br><br>    b:destination="id('main-content')" <br><br>    b:mode="aslastchild" /&gt;<br><br></pre><p>下面是用于从文件系统把类别加载为XML的BXS代码：</p><pre class="code">&lt;bsd:pipeline equals="categories.xml"<br><br>                                 access="public"&gt;<br><br>    &lt;bsd:readxml input="file:/categories.xml"/&gt;<br><br>&lt;/bsd:pipeline&gt;<br></pre><p>下面是用于创建选项卡式导航的BPC客户端模板：</p><pre class="code">&lt;b:tabrow&gt;<br><br>  &lt;s:for-each b:select="categories/category"&gt;<br><br>    &lt;b:tab&gt;<br><br>      &lt;s:attribute b:name="b:followstate"&gt;<br><br>        id('&lt;s:value-of b:select="name"/&gt;')<br><br>      &lt;/s:attribute&gt;<br><br>      &lt;s:value-of b:select="name"/&gt;<br><br>    &lt;/b:tab&gt;<br><br>  &lt;/s:for-each&gt;<br><br>&lt;/b:tabrow&gt;<br></pre><p>所有BPC代码（用蓝色表示）都在客户端执行，而所有BXS代码（用红色表示）都在服务器端执行。注意，在本例中，我选择了在客户端进行转换，因为
数据集很小。下面我会给出一个在服务器端转换的例子。两种转换都要用到XSLT语法。Backbase的一个强大功能就是，前端开发人员可以根据情况选择
在客户端还是服务器端处理表示逻辑。语法似乎允许轻松地把代码从客户端移到服务器端，或者反之。</p><p>以上的代码示例应该可以使您了解到，借助于Backbase，Ajax编程变得多么轻松。结合了DHTML的声明性方法则更容易上手。使用附加的B
标签不仅可以使界面更加丰富，而且可以使开发人员的效率更高。诸如&lt;b:tab&gt;之类的单个标签可以代替多行HTML和JavaScript
代码，而且保证可以用于各种浏览器。</p><li>默认数据</li><p>显示商店前端时，默认情况下显示的是狗的类别。对于本案例，BXS负责此项操作。BXS从一个Web服务获得数据，将其放入缓存，然后生成BXML
表示代码，再把这些表示代码发回给BPC。服务器还通过一项配置设置确定一个页面上可以显示的动物数量，并根据需要加入了Next和Previous按
钮。最后，服务器还提供了按照名称或价格进行排序的功能。</p><p>下面的代码片断演示了服务器功能。外部管道products-overview.xml首先调用catalog.xml子管道。该子管道要么返回缓
存中的宠物信息，要么调用另一个子管道catalog.ws。在缓存没有命中的情况下，内部管道catalog.ws会从Web服务获取宠物信息。</p><p>外部管道获得宠物信息，然后进行XSLT转换，从而以4x2表格显示这些信息，并带有Next和Previouse按钮，然后把BXML格式的代码发回给BPC。BPC呈现它接收到的BXML。</p><p>有3个嵌套的BXS管道分别用于从Web服务获取数据、将其放入缓存，以及通过XSLT转换创建BXML输出：
</p><pre class="code">&lt;bsd:pipeline equals="products-overview.xml"<br><br>                              access="public"/&gt;<br><br>  &lt;bsd:callpipe pipe="catalog.xml"/&gt;<br></pre><pre class="code">&lt;bsd:pipeline equals="catalog.xml" access="private"&gt;<br><br>  &lt;bsd:exist field="{global:petstore-catalog}"&gt;<br><br>    &lt;bsd:readxml&gt;{global:petstore-catalog}<br><br>    &lt;/bsd:readxml&gt;<br><br>    &lt;bsd:otherwise&gt;<br><br>      &lt;bsd:callpipe pipe="catalog.ws"/&gt;<br></pre><pre class="code">&lt;bsd:pipeline equals="catalog.ws"<br><br>                               access="private"&gt;<br><br>  &lt;bsd:try&gt;<br><br>    &lt;bsd:callws wsdl="PetstoreCatalog.wsdl"<br><br>                               method="getAll"/&gt;<br><br>    &lt;bsd:callpipe pipe="strip-root-ns"/&gt;<br><br>    &lt;bsd:catch&gt;<br><br>      &lt;bsd:xslt xslt="error.xslt"&gt;<br><br>        &lt;bsd:param name="errormsg"&gt;{error:message}<br><br>        &lt;/bsd:param&gt;<br><br>        &lt;bsd:param name="errorsrc"&gt;{error:source}<br><br>        &lt;/bsd:param&gt;<br><br>      &lt;/bsd:xslt&gt;<br><br>    &lt;/bsd:catch&gt;<br><br>  &lt;/bsd:try&gt;<br><br>&lt;/bsd:pipeline&gt;<br>      &lt;bsd:writexml&gt;{global:petstore-catalog}<br><br>      &lt;/bsd:writexml&gt;<br><br>    &lt;/bsd:otherwise&gt;<br><br>  &lt;/bsd:exist&gt;<br><br>&lt;/bsd:pipeline&gt;<br>&lt;bsd:extractfilter xpath=<br><br>  "category[name/text()='{requestparam:category}']"/&gt; <br><br>  &lt;bsd:xslt xslt="products/products-overview.xslt"&gt;<br><br>    &lt;bsd:param name="category"&gt;<br><br>      {requestparam:category}<br><br>    &lt;/bsd:param&gt;<br><br>    &lt;bsd:param name="stepsize"&gt;<br><br>      {global:stepsize}<br><br>    &lt;/bsd:param&gt;<br><br>    &lt;bsd:param name="sortorder"&gt;<br><br>      {requestparam:sortorder}<br><br>    &lt;/bsd:param&gt;<br><br>    &lt;bsd:param name="sortfield"&gt;<br><br>      {requestparam:sortfield}<br><br>    &lt;/bsd:param&gt;<br><br>  &lt;/bsd:xslt&gt;<br><br>&lt;/bsd:pipeline&gt;<br></pre><p>代码示例再次清楚地说明了，借助于Backbase，以声明性的方式创建Ajax前端是多么容易的事情。例如，只要使用带有一个WSDL引用作为属性的&lt;bsd:callws&gt;标签，就可以调用一个Web服务。</p><li>用户交互</li><p>现在，最终用户可以与宠物商店类别进行交互。可以使用Next或Previous按钮或者排序功能在动物类别中进行浏览。或者，只要点击一下相应的选项卡，就可以转到另一个类别中。</p><p>BPC和BXS对这种交互进行了无缝处理。显示已经在客户端上的数据时，无需与服务器进行任何通信。例如，购物者已经从狗类别转到了猫类别，然后再
回到狗类别。客户端仍然拥有狗类别的数据，所以可以马上显示出来，这使得购物体验变得更完美。其他的类别需要从BXS获取。BXS要么立即从其缓存返回它
们，要们访问Web服务来获得新数据。</p>
</ul>

 
<p>　　为了详细说明Backbase Ajax宠物商店的实现，我把重点放在了初始化的步骤上。完整的宠物商店（可以从<a href="http://www.backbase.com/xmlserver" target="_blank">http://www.backbase.com/xmlserver</a>下载）还包括以下功能：</p>

<ul>
<ul><li>商店前端</li><ul><li>初始化。</li><li>使用从文件加载的宠物类别创建选项卡。</li><li>默认情况下从Web服务加载Dog选项卡。</li><li>通过缓存浏览Dog并对其进行排序。</li></ul><li>宠物详细情况</li><ul><li>使用跟踪聚合来自缓存和数据库的宠物详细情况。</li><li>创建可视化历史记录。</li></ul><li>购物车</li><ul><li>使用跟踪添加到购物车。</li></ul><li>登录</li><ul><li>登录和身份验证。</li></ul><li>退出</li><ul><li>退出和授权。</li><li>确认。</li></ul></ul>
</ul>

<p><strong>结束语</strong></p>

<p>　　最近有很多人都在研究Ajax。Ajax的优点已经在实践中得到了证明。定制Ajax的缺点在于它的复杂性和不兼容性。大量客户端
JavaScript的出现意味着开发人员很可能陷入到浏览器实现差别的泥潭中去。另外，JavaScript这种语言不适用于复杂的应用程序。</p>

<p>　　为了开发易于管理的、可伸缩的和适应未来变化的Ajax解决方案，开发人员所需使用的工具应该具有比定制部件开发更多的功能。Backbase
Ajax软件提供了一个功能全面的客户端GUI管理引擎(Backbase Presentation
Client)、一个灵活的服务器端XML管道(Backbase XML
Server)和一种声明性的基于标签的UI语言，BXML(Backbase eXtensible Markup
Language)。该方法具有几个优点。</p>

<p>　　首先，Backbae易于使用。它的声明性语言水平地扩展了DHTML；它完全对开发人员隐藏了浏览器兼容性的问题；而且它带有一套开发和调试工具。</p>

<p>　　其次，Backbase是一个功能全面的Ajax
GUI管理系统。Backbase的先进性大大超过了其他Ajax框架，它完全把重点放在提供一个部件库或客户端－服务器通信（如DWR）上。在控件和客
户端－服务器通信的基础上，Backbase提供了用于如下用途的标签：提供电影效果，随需应变的数据加载，数据绑定和客户端的数据转换，对于Back和
Forward按钮的支持，完善的GUI状态管理，等等。所有这些功能对于目前的Ajax Web应用程序来说都是必需的。</p>

<p>　　最后，Backbase是以兼容的方式提供所有客户端和服务器端的功能。用户可以使用富Ajax前端扩展现有的应用程序，同时无需修改后端。对于整个表示层来说，它的架构是时新的、模块化的，而且它基于XML。</p>

<p><strong>参考资料</strong></p>

<ul>
<li><a href="http://java.sun.com/developer/releases/petstore/" target="_blank">Java Pet Store Demo</a>。</li><li><a href="http://www.adaptivepath.com/publications/essays/archives/000385.php" target="_blank">Ajax: A New Approach to Web Applications</a>，作者Jesse James Garrett（Adaptive Path，2005年2月）。</li>
</ul>

<p><strong>原文出处</strong></p>

<p>A Backbase Ajax Front-end for J2EE Applications</p>

<p><a href="http://dev2dev.bea.com/pub/a/2005/08/backbase_ajax.html" target="_blank">http://dev2dev.bea.com/pub/a/2005/08/backbase_ajax.html</a>
</p>


			      <!--文章其他信息-->
			       
<div class="dot001"><img src="http://dev2dev.bea.com.cn/images/_.gif" alt="" height="1" width="100%"></div>

                     
                     
<table border="0" cellpadding="3" cellspacing="0" width="100%">
<tbody><tr valign="bottom">
                       <td colspan="2" height="20">&nbsp;<span class="h2b">作者简介</span></td>
                     </tr><tr><td align="center" valign="top" width="0"><br>
</td><td><a href="http://dev2dev.bea.com/pub/au/324" target="_blank">Mark Schiefelbein</a>自2005年2月以来一直担任Backbase的产品管理主管。Mark极大地推动了Backbase Rich Internet Application的全球推广。</td></tr></tbody>
</table>
<img src ="http://www.blogjava.net/ericwang/aggbug/21178.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ericwang/" target="_blank">Dion</a> 2005-11-23 20:31 <a href="http://www.blogjava.net/ericwang/archive/2005/11/23/21178.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转载]AJAX开发简略续一</title><link>http://www.blogjava.net/ericwang/archive/2005/11/23/21177.html</link><dc:creator>Dion</dc:creator><author>Dion</author><pubDate>Wed, 23 Nov 2005 12:28:00 GMT</pubDate><guid>http://www.blogjava.net/ericwang/archive/2005/11/23/21177.html</guid><wfw:comment>http://www.blogjava.net/ericwang/comments/21177.html</wfw:comment><comments>http://www.blogjava.net/ericwang/archive/2005/11/23/21177.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ericwang/comments/commentRss/21177.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ericwang/services/trackbacks/21177.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 原文：http://www.blogjava.net/eamoi/archive/2005/11/07/18566.html（续前一篇）AJAX开发简略在前一篇文章《AJAX开发简略》中，我们讲述了如何用AJAX来改进设计的用户体验。接下来，我们将讲述如何用AJAX来更新文档，以及处理服务器返回的XML文档。我们的最终目的是接收服务器的返回信息，修改当前文档的内容。是时候让...&nbsp;&nbsp;<a href='http://www.blogjava.net/ericwang/archive/2005/11/23/21177.html'>阅读全文</a><img src ="http://www.blogjava.net/ericwang/aggbug/21177.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ericwang/" target="_blank">Dion</a> 2005-11-23 20:28 <a href="http://www.blogjava.net/ericwang/archive/2005/11/23/21177.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转载]《AJAX开发简略》配文代码</title><link>http://www.blogjava.net/ericwang/archive/2005/11/23/21176.html</link><dc:creator>Dion</dc:creator><author>Dion</author><pubDate>Wed, 23 Nov 2005 12:26:00 GMT</pubDate><guid>http://www.blogjava.net/ericwang/archive/2005/11/23/21176.html</guid><wfw:comment>http://www.blogjava.net/ericwang/comments/21176.html</wfw:comment><comments>http://www.blogjava.net/ericwang/archive/2005/11/23/21176.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ericwang/comments/commentRss/21176.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ericwang/services/trackbacks/21176.html</trackback:ping><description><![CDATA[	
	
<p>原文：http://www.blogjava.net/eamoi/archive/2005/11/01/17639.html<br>
<br>
</p>
<p>有网友反映说《AJAX开发简略》配文代码不全。其实应该是全的，只是要把包括框架和两个示例的程序都整合起来看。这里把全部的代码贴出来，需要的朋友可以看看。<br>sample1_1.jsp：<br>&lt;%@ page contentType="text/html; charset=gb2312" language="java" errorPage="" %&gt;<br>&lt;html&gt;<br>&lt;head&gt;<br>&lt;meta http-equiv="Content-Type" content="text/html; charset=gb2312"&gt;<br>&lt;title&gt;无标题文档&lt;/title&gt;<br>&lt;script language="javascript"&gt;<br>&nbsp;var http_request = false;<br>&nbsp;function send_request(url) {//初始化、指定处理函数、发送请求的函数<br>&nbsp;&nbsp;http_request = false;<br>&nbsp;&nbsp;//开始初始化XMLHttpRequest对象<br>&nbsp;&nbsp;if(window.XMLHttpRequest) { //Mozilla 浏览器<br>&nbsp;&nbsp;&nbsp;http_request = new XMLHttpRequest();<br>&nbsp;&nbsp;&nbsp;if (http_request.overrideMimeType) {//设置MiME类别<br>&nbsp;&nbsp;&nbsp;&nbsp;http_request.overrideMimeType('text/xml');<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;else if (window.ActiveXObject) { // IE浏览器<br>&nbsp;&nbsp;&nbsp;try {<br>&nbsp;&nbsp;&nbsp;&nbsp;http_request = new ActiveXObject("Msxml2.XMLHTTP");<br>&nbsp;&nbsp;&nbsp;} catch (e) {<br>&nbsp;&nbsp;&nbsp;&nbsp;try {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;http_request = new ActiveXObject("Microsoft.XMLHTTP");<br>&nbsp;&nbsp;&nbsp;&nbsp;} catch (e) {}<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;if (!http_request) { // 异常，创建对象实例失败<br>&nbsp;&nbsp;&nbsp;window.alert("不能创建XMLHttpRequest对象实例.");<br>&nbsp;&nbsp;&nbsp;return false;<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;http_request.onreadystatechange = processRequest;<br>&nbsp;&nbsp;// 确定发送请求的方式和URL以及是否同步执行下段代码<br>&nbsp;&nbsp;http_request.open("GET", url, true);<br>&nbsp;&nbsp;http_request.send(null);<br>&nbsp;}<br>&nbsp;// 处理返回信息的函数<br>&nbsp;&nbsp;&nbsp; function processRequest() {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (http_request.readyState == 4) { // 判断对象状态<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (http_request.status == 200) { // 信息已经成功返回，开始处理信息<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; alert(http_request.responseText);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else { //页面不正常<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; alert("您所请求的页面有异常。");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;function userCheck() {<br>&nbsp;&nbsp;var f = document.form1;<br>&nbsp;&nbsp;var username = f.username.value;<br>&nbsp;&nbsp;if(username=="") {<br>&nbsp;&nbsp;&nbsp;window.alert("用户名不能为空。");<br>&nbsp;&nbsp;&nbsp;f.username.focus();<br>&nbsp;&nbsp;&nbsp;return false;<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;else {<br>&nbsp;&nbsp;&nbsp;send_request('sample1_2.jsp?username='+username);<br>&nbsp;&nbsp;}<br>&nbsp;}<br>&lt;/script&gt;<br>&lt;link href="css/style.css" rel="stylesheet" type="text/css"&gt;<br>&lt;/head&gt;</p>


<p>&lt;body&gt;<br>&lt;form name="form1" action="" method="post"&gt;<br>用户名：&lt;input type="text" name="username" value=""&gt;&amp;nbsp;<br>&lt;input type="button" name="check" value="唯一性检查" onClick="userCheck()"&gt;<br>&lt;input type="submit" name="submit" value="提交"&gt;<br>&lt;/form&gt;<br>&lt;!--span
style="cursor: pointer; text-decoration: underline"
onclick="send_request('2.jsp?username=educhina')"&gt;Send a
request&lt;/span--&gt;<br>&lt;/body&gt;<br>&lt;/html&gt;<br><br>sample1_2.jsp：<br>&lt;%@ page contentType="text/html; charset=gb2312" language="java" errorPage="" %&gt;<br>&lt;%<br>String playPos = request.getParameter("playPos");<br>if("pos_1".equals(playPos)) out.print("用户名已经被注册，请更换一个用户名。");<br>else out.print("用户名尚未被使用，您可以继续。");<br>%&gt;<br><br>sample2_1.jsp：<br>&lt;%@ page contentType="text/html; charset=gb2312" language="java" errorPage="" %&gt;<br>&lt;html&gt;<br>&lt;head&gt;<br>&lt;meta http-equiv="Content-Type" content="text/html; charset=gb2312"&gt;<br>&lt;title&gt;无标题文档&lt;/title&gt;<br>&lt;script language="javascript"&gt;<br>&nbsp;var http_request = false;<br>&nbsp;var currentPos = null;<br>&nbsp;function send_request(url) {//初始化、指定处理函数、发送请求的函数<br>&nbsp;&nbsp;http_request = false;<br>&nbsp;&nbsp;//开始初始化XMLHttpRequest对象<br>&nbsp;&nbsp;if(window.XMLHttpRequest) { //Mozilla 浏览器<br>&nbsp;&nbsp;&nbsp;http_request = new XMLHttpRequest();<br>&nbsp;&nbsp;&nbsp;if (http_request.overrideMimeType) {//设置MiME类别<br>&nbsp;&nbsp;&nbsp;&nbsp;http_request.overrideMimeType('text/xml');<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;else if (window.ActiveXObject) { // IE浏览器<br>&nbsp;&nbsp;&nbsp;try {<br>&nbsp;&nbsp;&nbsp;&nbsp;http_request = new ActiveXObject("Msxml2.XMLHTTP");<br>&nbsp;&nbsp;&nbsp;} catch (e) {<br>&nbsp;&nbsp;&nbsp;&nbsp;try {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;http_request = new ActiveXObject("Microsoft.XMLHTTP");<br>&nbsp;&nbsp;&nbsp;&nbsp;} catch (e) {}<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;if (!http_request) { // 异常，创建对象实例失败<br>&nbsp;&nbsp;&nbsp;window.alert("不能创建XMLHttpRequest对象实例.");<br>&nbsp;&nbsp;&nbsp;return false;<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;http_request.onreadystatechange = processRequest;<br>&nbsp;&nbsp;// 确定发送请求的方式和URL以及是否同步执行下段代码<br>&nbsp;&nbsp;http_request.open("GET", url, true);<br>&nbsp;&nbsp;http_request.send(null);<br>&nbsp;}<br>&nbsp;// 处理返回信息的函数<br>&nbsp;&nbsp;&nbsp; function processRequest() {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (http_request.readyState == 4) { // 判断对象状态<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (http_request.status == 200) { // 信息已经成功返回，开始处理信息<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //alert(http_request.responseText);<br>&nbsp;&nbsp;&nbsp;&nbsp;document.getElementById(currentPos).innerHTML = http_request.responseText;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else { //页面不正常<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; alert("您所请求的页面有异常。");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;//显示部门下的岗位<br>&nbsp;function showRoles(obj) {<br>&nbsp;&nbsp;document.getElementById(obj).parentNode.style.display = "";<br>&nbsp;&nbsp;document.getElementById(obj).innerHTML = "正在读取数据..."<br>&nbsp;&nbsp;currentPos = obj;<br>&nbsp;&nbsp;send_request("sample2_2.jsp?playPos="+obj);<br>&nbsp;}<br>&lt;/script&gt;<br>&lt;link href="css/style.css" rel="stylesheet" type="text/css"&gt;<br>&lt;/head&gt;</p>

<p>&lt;body&gt;<br>&lt;table width="200" border="0" cellspacing="0" cellpadding="0"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;tr&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;td height="20"&gt;&lt;a href="javascript:void(0)"
onClick="showRoles('pos_1')"&gt;经理室&lt;/a&gt;&lt;/td&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/tr&gt;<br>&nbsp;&nbsp;&nbsp; &lt;tr style="display:none"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td height="20" id="pos_1"&gt;&amp;nbsp;&lt;/td&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/tr&gt;<br>&nbsp;&nbsp;&nbsp; &lt;tr&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;td height="20"&gt;&lt;a href="javascript:void(0)"
onClick="showRoles('pos_2')"&gt;开发部&lt;/a&gt;&lt;/td&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/tr&gt;<br>&nbsp;&nbsp;&nbsp; &lt;tr style="display:none "&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td id="pos_2" height="20"&gt;&amp;nbsp;&lt;/td&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/tr&gt;<br>&lt;/table&gt;<br>&lt;!--a href="javascript:void(0)" onClick="showRoles('pos_1')"&gt;测试&lt;/a--&gt;<br>&lt;!--span
style="cursor: pointer; text-decoration: underline"
onclick="send_request('2.jsp?username=educhina')"&gt;Send a
request&lt;/span--&gt;<br>&lt;/body&gt;<br>&lt;/html&gt;<br><br>sample2_2.jsp：<br>&lt;%@ page contentType="text/html; charset=gb2312" language="java" errorPage="" %&gt;<br>&lt;%<br>String playPos = request.getParameter("playPos");<br>if("pos_1".equals(playPos)) out.print("&amp;nbsp;&amp;nbsp;总经理&lt;br&gt;&amp;nbsp;&amp;nbsp;副总经理");<br>else if("pos_2".equals(playPos)) out.println("&amp;nbsp;&amp;nbsp;总工程师&lt;br&gt;&amp;nbsp;&amp;nbsp;软件工程师");<br>%&gt;</p>

	<img src ="http://www.blogjava.net/ericwang/aggbug/21176.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ericwang/" target="_blank">Dion</a> 2005-11-23 20:26 <a href="http://www.blogjava.net/ericwang/archive/2005/11/23/21176.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转载]AJAX开发简略</title><link>http://www.blogjava.net/ericwang/archive/2005/11/23/21175.html</link><dc:creator>Dion</dc:creator><author>Dion</author><pubDate>Wed, 23 Nov 2005 12:25:00 GMT</pubDate><guid>http://www.blogjava.net/ericwang/archive/2005/11/23/21175.html</guid><wfw:comment>http://www.blogjava.net/ericwang/comments/21175.html</wfw:comment><comments>http://www.blogjava.net/ericwang/archive/2005/11/23/21175.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ericwang/comments/commentRss/21175.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ericwang/services/trackbacks/21175.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: http://www.blogjava.net/eamoi/archive/2005/10/31/17489.htmlAJAX开发简略&nbsp;文档说明&nbsp;参与人员：&nbsp;作者网名联络柯自聪eamoi&nbsp;&nbsp; educhina...&nbsp;&nbsp;<a href='http://www.blogjava.net/ericwang/archive/2005/11/23/21175.html'>阅读全文</a><img src ="http://www.blogjava.net/ericwang/aggbug/21175.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ericwang/" target="_blank">Dion</a> 2005-11-23 20:25 <a href="http://www.blogjava.net/ericwang/archive/2005/11/23/21175.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Ajax简介</title><link>http://www.blogjava.net/ericwang/archive/2005/11/23/21114.html</link><dc:creator>Dion</dc:creator><author>Dion</author><pubDate>Wed, 23 Nov 2005 04:53:00 GMT</pubDate><guid>http://www.blogjava.net/ericwang/archive/2005/11/23/21114.html</guid><wfw:comment>http://www.blogjava.net/ericwang/comments/21114.html</wfw:comment><comments>http://www.blogjava.net/ericwang/archive/2005/11/23/21114.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ericwang/comments/commentRss/21114.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ericwang/services/trackbacks/21114.html</trackback:ping><description><![CDATA[转自http://dev2dev.bea.com.cn/techdoc/2005110103.html<br>
<br>
<br>
<span class="h1b">Ajax简介</span><br>


<br>

<table border="0" cellpadding="0" cellspacing="0" width="100%">

  <tbody><tr>
    <td height="64">时间：2005-11-01<br>
作者：<a href="http://dev2dev.bea.com.cn/author/353.html">David Teare</a><br>
浏览次数：
<script language="JavaScript" type="text/JavaScript" src="http://203.81.25.103/cgi-bin/beadevcount.cgi?d_id=681"></script>1786
<br>
本文关键字：<a href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords=ajax">ajax</a>,&nbsp;<a href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords=dhtml">dhtml</a>,&nbsp;<a href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords=dwr">dwr</a>,&nbsp;<a href="http://dev2dev.bea.com.cn/products/search.jsp?searchtype=keywords&amp;keywords=%20javascript"> javascript</a></td>
    <td><table class="box_content" border="0" cellpadding="0" cellspacing="0">
      <tbody><tr>
        <td><span class="h2b">文章工具</span><br>
            <img src="http://dev2dev.bea.com.cn/images/letter001.gif" alt="推荐给朋友" align="middle" height="10" width="19">&nbsp;<a href="javascript:sendmail()">推荐给朋友</a><br>
            <img src="http://dev2dev.bea.com.cn/images/print001.gif" alt="打印文章" align="middle" height="18" width="19">&nbsp;<a href="javascript:window.print()">打印文章</a></td>
      </tr>
    </tbody></table></td>
  </tr>
</tbody>
</table>


<!-- 提取技术文章 -->
<div class="beas"><img src="http://dev2dev.bea.com.cn/images/dot6B6B6B.gif" alt="" height="1" width="100%"></div>


<p>　　作为J2EE开发人员，我们似乎经常关注“后端机制（backend
mechanics）”。我们通常会忘记，J2EE的主要成功之处在Web应用程序方面；许多原因使得人们喜欢利用Web开发应用程序，但主要还是因为其
易于部署的特点允许站点以尽可能低的成本拥有上百万的用户。遗憾的是，在过去几年中，我们在后端投入了太多的时间，而在使我们的Web用户界面对用户自然
和响应灵敏方面却投入不足。</p>

<p>　　本文介绍一种方法，Ajax，使用它可以构建更为动态和响应更灵敏的Web应用程序。该方法的关键在于对浏览器端的JavaScript、
DHTML和与服务器异步通信的组合。本文也演示了启用这种方法是多么简单：利用一个Ajax框架（指DWR）构造一个应用程序，它直接从浏览器与后端服
务进行通信。如果使用得当，这种强大的力量可以使应用程序更加自然和响应灵敏，从而提升用户的浏览体验。</p>

<p>　　该应用程序中所使用的示例代码已打包为单独的WAR文件，可供下载。</p>

<p><strong>简介</strong></p>

<p>　　术语Ajax用来描述一组技术，它使浏览器可以为用户提供更为自然的浏览体验。在Ajax之前，Web站点强制用户进入提交/等待/重新显示范
例，用户的动作总是与服务器的“思考时间”同步。Ajax提供与服务器异步通信的能力，从而使用户从请求/响应的循环中解脱出来。借助于Ajax，可以在
用户单击按钮时，使用JavaScript和DHTML立即更新UI，并向服务器发出异步请求，以执行更新或查询数据库。当请求返回时，就可以使用
JavaScript和CSS来相应地更新UI，而不是刷新整个页面。最重要的是，用户甚至不知道浏览器正在与服务器通信：Web站点看起来是即时响应
的。</p>

<p>　　虽然Ajax所需的基础架构已经出现了一段时间，但直到最近异步请求的真正威力才得到利用。能够拥有一个响应极其灵敏的Web站点确实激动人
心，因为它最终允许开发人员和设计人员使用标准的HTML/CSS/JavaScript堆栈创建“桌面风格的（desktop-like）”可用性。</p>

<p>　　通常，在J2EE中，开发人员过于关注服务和持久性层的开发，以至于用户界面的可用性已经落后。在一个典型的J2EE开发周期中，常常会听到这样的话，“我们没有可投入UI的时间”或“不能用HTML实现”。但是，以下Web站点证明，这些理由再也站不住脚了：</p>

<ul>
<li><a href="http://backpackit.com/" target="_blank">BackPack</a></li><li><a href="http://www.google.com/webhp?complete=1&amp;hl=en" target="_blank">Google Suggest</a></li><li><a href="http://maps.google.com/" target="_blank">Google Maps</a></li><li><a href="http://www.palmsphere.com/" target="_blank">PalmSphere</a></li>
</ul>

<p>　　所有这些Web站点都告诉我们，Web应用程序不必完全依赖于从服务器重新载入页面来向用户呈现更改。一切似乎就在瞬间发生。简而言之，在涉及到用户界面的响应灵敏度时，基准设得更高了。</p>

<p><strong>定义Ajax</strong></p>

<p>　　Adaptive Path公司的Jesse James Garrett这样<a href="http://www.adaptivepath.com/publications/essays/archives/000385.php" target="_blank">定义Ajax</a>：</p>

<p>　　Ajax不是一种技术。实际上，它由几种蓬勃发展的技术以新的强大方式组合而成。Ajax包含：</p>

<ul>
<li>基于<a href="http://www.w3.org/TR/xhtml1/" target="_blank">XHTML</a>和<a href="http://www.w3.org/Style/CSS/" target="_blank">CSS</a>标准的表示；</li><li>使用<a href="http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/introduction.html" target="_blank">Document Object Model</a>进行动态显示和交互；</li><li>使用XMLHttpRequest与服务器进行异步通信；</li><li>使用JavaScript绑定一切。</li>
</ul>

<p>　　这非常好，但为什么要以Ajax命名呢？其实术语Ajax是由Jesse James Garrett创造的，他说它是“Asynchronous JavaScript + XML的简写”。</p>

<p><strong>Ajax的工作原理</strong></p>

<p>　　Ajax的核心是JavaScript对象XmlHttpRequest。该对象在Internet Explorer
5中首次引入，它是一种支持异步请求的技术。简而言之，XmlHttpRequest使您可以使用JavaScript向服务器提出请求并处理响应，而不
阻塞用户。</p>

<p>　　在创建Web站点时，在客户端执行屏幕更新为用户提供了很大的灵活性。下面是使用Ajax可以完成的功能：</p>

<ul>
<li>动态更新购物车的物品总数，无需用户单击Update并等待服务器重新发送整个页面。</li><li>提升站点的性
能，这是通过减少从服务器下载的数据量而实现的。例如，在Amazon的购物车页面，当更新篮子中的一项物品的数量时，会重新载入整个页面，这必须下载
32K的数据。如果使用Ajax计算新的总量，服务器只会返回新的总量值，因此所需的带宽仅为原来的百分之一。</li><li>消除了每次用户输入时的页面刷新。例如，在Ajax中，如果用户在分页列表上单击Next，则服务器数据只刷新列表而不是整个页面。</li><li>直接编辑表格数据，而不是要求用户导航到新的页面来编辑数据。对于Ajax，当用户单击Edit时，可以将静态表格刷新为内容可编辑的表格。用户单击Done之后，就可以发出一个Ajax请求来更新服务器，并刷新表格，使其包含静态、只读的数据。</li>
</ul>

<p>　　一切皆有可能！但愿它能够激发您开始开发自己的基于Ajax的站点。然而，在开始之前，让我们介绍一个现有的Web站点，它遵循传统的提交/等待/重新显示的范例，我们还将讨论Ajax如何提升用户体验。</p>

<p><strong>Ajax可用于那些场景？——一个例子：MSN Money页面</strong></p>

<p>　　前几天，在浏览MSN Money页面的时候，有一篇<a href="http://moneycentral.msn.com/content/Investing/Realestate/P63633.asp" target="_blank">关于房地产投资的文章</a>引起了我的好奇心。我决定使用站点的“Rate this article”（评价本文）功能，鼓励其他的用户花一点时间来阅读这篇文章。在我单击vote按钮并等待了一会儿之后，整个页面被刷新，在原来投票问题所在的地方出现了一个漂亮的感谢画面。</p>

<p> <img src="http://dev2dev.bea.com.cn/images/051101/0511010101.jpg" height="63" width="303"> </p>

<p>　　而Ajax能够使用户的体验更加愉快，它可以提供响应更加灵敏的UI，并消除页面刷新所带来的闪烁。目前，由于要刷新整个页面，需要传送大量的
数据，因为必须重新发送整个页面。如果使用Ajax，服务器可以返回一个包含了感谢信息的500字节的消息，而不是发送26,813字节的消息来刷新整个
页面。即使使用的是高速Internet，传送26K和1/2K的差别也非常大。同样重要的是，只需要刷新与投票相关的一小节，而不是刷新整个屏幕。</p>

<p>　　让我们利用Ajax实现自己的基本投票系统。</p>

<p><strong>原始的Ajax：直接使用XmlHttpRequest</strong></p>

<p>　　如上所述，Ajax的核心是JavaScript对象XmlHttpRequest。下面的示例文章评价系统将带您熟悉Ajax的底层基本知识：<a href="http://tearesolutions.com/ajax-demo/raw-ajax.html" target="_blank">http://tearesolutions.com/ajax-demo/raw-ajax.html</a>。注：如果您已经在本地WebLogic容器中安装了<a href="http://dev2dev.bea.com/2005/08/ajax-demo.war">ajax-demo.war</a>，可以导航到<a href="http://localhost:7001/ajax-demo/raw-ajax.html" target="_blank">http://localhost:7001/ajax-demo/raw-ajax.html</a>，</p>

<p>　　浏览应用程序，参与投票，并亲眼看它如何运转。熟悉了该应用程序之后，继续阅读，进一步了解其工作原理细节。</p>

　　首先，您拥有一些简单的定位点标记，它连接到一个JavaScriptcastVote(rank)函数。
<pre class="code">function castVote(rank) {<br>  var url = "/ajax-demo/static-article-ranking.html";<br>  var callback = processAjaxResponse;<br>  executeXhr(callback, url);<br>}<br></pre>

<p>　　该函数为您想要与之通信的服务器资源创建一个URL并调用内部函数executeXhr，提供一个回调JavaScript函数，一旦服务器响
应可用，该函数就被执行。由于我希望它运行在一个简单的Apache环境中，“cast vote
URL”只是一个简单的HTML页面。在实际情况中，被调用的URL将记录票数并动态地呈现包含投票总数的响应。</p>

　　下一步是发出一个XmlHttpRequest请求：
<pre class="code">function executeXhr(callback, url) {<br>  // branch for native XMLHttpRequest object<br>  if (window.XMLHttpRequest) {<br>    req = new XMLHttpRequest();<br>    req.onreadystatechange = callback;<br>    req.open("GET", url, true);<br>    req.send(null);<br>  } // branch for IE/Windows ActiveX version<br>  else if (window.ActiveXObject) {<br>    req = new ActiveXObject("Microsoft.XMLHTTP");<br>    if (req) {<br>      req.onreadystatechange = callback;<br>      req.open("GET", url, true);<br>      req.send();<br>    }<br>  }<br>}<br><br></pre>

<p>　　如您所见，执行一个XmlHttpRequest并不简单，但非常直观。和平常一样，在JavaScript领域，大部分的工作量都花在确保浏
览器兼容方面。在这种情况下，首先要确定XmlHttpRequest是否可用。如果不能用，很可能要使用Internet
Explorer，这样就要使用所提供的ActiveX实现。</p>

<p>executeXhr()方法中最关键的部分是这两行：
</p>

<pre class="code">req.onreadystatechange = callback;<br>req.open("GET", url, true);<br></pre>

<p>　　第一行定义了JavaScript回调函数，您希望一旦响应就绪它就自动执行，而req.open()方法中所指定的“true”标志说明您想要异步执行该请求。</p>

　　一旦服务器处理完XmlHttpRequest并返回给浏览器，使用req.onreadystatechange指派所设置的回调方法将被自动调用。
<pre class="code">function processAjaxResponse() {<br>  // only if req shows "loaded"<br>  if (req.readyState == 4) {<br>    // only if "OK"<br>    if (req.status == 200) {<br>      502 502'votes').innerHTML = req.responseText;<br>    } else {<br>      alert("There was a problem retrieving the XML data:<br>" +<br>      req.statusText);<br>    }<br>  }<br>} <br></pre>

<p>　　该代码相当简洁，并且使用了几个幻数，这使得难以一下子看出发生了什么。为了弄清楚这一点，下面的表格（引用自<a href="http://developer.apple.com/internet/webcontent/xmlhttpreq.html" target="_blank">http://developer.apple.com/internet/webcontent/xmlhttpreq.html</a>）列举了常用的XmlHttpRequest对象属性。</p>

<table bgcolor="#cccccc" border="0" cellpadding="0" cellspacing="1" width="80%">

  <tbody><tr bgcolor="#ffffff">
    <td height="22"><p><strong>属性</strong></p></td>
    <td><p><strong>描述</strong></p></td>
  </tr>
  <tr bgcolor="#ffffff">
    <td height="22" valign="top"><p>onreadystatechange</p></td>
    <td valign="top"><p>每次状态改变所触发事件的事件处理程序</p></td>
  </tr>
  <tr bgcolor="#ffffff">
    <td height="22" valign="top"><p>readyState</p></td>
    <td valign="top"><p>对象状态值：
        </p><ul><li>0 = 未初始化（uninitialized）</li><li>1 = 正在加载（loading）</li><li>2 = 加载完毕（loaded）</li><li>3 = 交互（interactive）</li><li>4 = 完成（complete）</li></ul></td>
  </tr>
  <tr bgcolor="#ffffff">
    <td height="22" valign="top"><p>responseText</p></td>
    <td valign="top"><p>从服务器进程返回的数据的字符串形式</p></td>
  </tr>
  <tr bgcolor="#ffffff">
    <td height="22" valign="top"><p>responseXML</p></td>
    <td valign="top"><p>从服务器进程返回的DOM兼容的文档数据对象</p></td>
  </tr>
  <tr bgcolor="#ffffff">
    <td height="22" valign="top"><p>status</p></td>
    <td valign="top"><p>从服务器返回的数字代码，比如404（未找到）或200（就绪）</p></td>
  </tr>
  <tr bgcolor="#ffffff">
    <td height="22" valign="top"><p>statusText</p></td>
    <td valign="top"><p>伴随状态码的字符串信息</p></td>
  </tr>
</tbody>
</table>

<p>　　现在processVoteResponse()函数开始显示出其意义了。它首先检查XmlHttpRequest的整体状态以保证它已经完成
（readyStatus == 4），然后根据服务器的设定询问请求状态。如果一切正常（status ==
200）,就使用innerHTML属性重写DOM的“votes”节点的内容。</p>

<p>　　既然您亲眼看到了XmlHttpRequest对象是如何工作的，就让我们利用一个旨在简化JavaScript与Java应用程序之间的异步通信的框架来对具体的细节进行抽象。</p>

<p><strong>Ajax: DWR方式</strong></p>

<p>　　按照与文章评价系统相同的流程，我们将使用Direct Web Remoting（DWR）框架实现同样的功能。</p>

<p>　　假定文章和投票结果存储在一个数据库中，使用某种对象/关系映射技术来完成抽取工作。为了部署起来尽可能地简单，我们不会使用数据库进行持久性
存储。此外，为使应用程序尽可能通用，也不使用Web框架。相反，应用程序将从一个静态HTML文件开始，可以认为它由服务器动态地呈现。除了这些简化措
施，应用程序还应该使用Spring Framework关联一切，以便轻松看出如何在一个“真实的”应用程序中使用DWR。</p>

<p>　　现在应该下载示例应用程序并熟悉它。该应用程序被压缩为标准的WAR文件，因此您可以把它放置到任何一个Web容器中——无需进行配置。部署完毕之后，就可以导航到<a href="http://localhost:7001/ajax-demo/dwr-ajax.html" target="_blank">http://localhost:7001/ajax_demo/dwr-ajax.html</a>来运行程序。</p>

<p>　　可以查看<a href="http://dev2dev.bea.com/2005/08/source.html" target="_blank">HTML 源代码</a>，了解它如何工作。给人印象最深的是，代码如此简单——所有与服务器的交互都隐藏在JavaScript对象ajaxSampleSvc的后面。更加令人惊讶的是，ajaxSampleSvc服务不是由手工编写而是完全自动生成的！让我们继续，看看这是如何做到的。</p>

<p><strong>引入DWR</strong></p>

<p>　　如同在“原始的Ajax”一节所演示的那样，直接使用XmlHttpRequest创建异步请求非常麻烦。不仅JavaScript代码冗长，而且必须考虑服务器端为定位Ajax请求到适当的服务所需做的工作，并将结果封送到浏览器。</p>

<p>　　设计DWR的目的是要处理将Web页面安装到后端服务上所需的所有信息管道。它是一个Java框架，可以很轻松地将它插入到Web应用程序中，
以便JavaScript代码可以调用服务器上的服务。它甚至直接与Spring
Framework集成，从而允许用户直接向Web客户机公开bean。</p>

<p>　　DWR真正的巧妙之处是，在用户配置了要向客户机公开的服务之后，它使用反射来生成JavaScript对象，以便Web页面能够使用这些对象
来访问该服务。然后Web页面只需接合到生成的JavaScript对象，就像它们是直接使用服务一样；DWR无缝地处理所有有关Ajax和请求定位的琐
碎细节。</p>

<p>　　让我们仔细分析一下示例代码，弄清它是如何工作的。</p>

<p><strong>应用程序细节：DWR分析</strong></p>

<p>　　关于应用程序，首先要注意的是，它是一个标准的Java应用程序，使用分层架构（Layered Architecture）设计模式。使用DWR通过JavaScript公开一些服务并不影响您的设计。
</p>

<p> <img src="http://dev2dev.bea.com.cn/images/051101/0511010102.jpg" height="344" width="209"> </p>

<p>　　下面是一个简单的Java服务，我们将使用DWR框架直接将其向JavaScript代码公开：</p>

<pre class="code">package com.tearesolutions.service;<br><br>public interface AjaxSampleSvc { <br>  Article castVote(int rank);<br>}<br></pre>

<p>　　这是一个被简化到几乎不可能的程度的例子，其中只有一篇文章可以投票。该服务由Spring管理，它使用的bean名是ajaxSampleSvc，它的持久性需求则依赖于ArticleDao。详情请参见applicationContext.xml。</p>

<p>　　为了把该服务公开为JavaScript对象，需要配置DWR，添加dwr.xml文件到WEB-INF目录下：
</p>

<pre class="code">&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br>&lt;!DOCTYPE dwr PUBLIC<br> "-//GetAhead Limited//DTD Direct Web Remoting 0.4//EN"<br> "http://www.getahead.ltd.uk/dwr/dwr.dtd"&gt;<br>	<br>&lt;dwr&gt;<br> &lt;allow&gt;<br>  &lt;create creator="spring" javascript="ajaxSampleSvc"&gt;<br>   &lt;param name="beanName" value="ajaxSampleSvc" /&gt;<br>  &lt;/create&gt;<br>  &lt;convert converter="bean" match="com.tearesolutions.model.Article"/&gt;<br>  &lt;exclude method="toString"/&gt;<br>  &lt;exclude method="setArticleDao"/&gt;<br> &lt;/allow&gt;<br>&lt;/dwr&gt;<br></pre>

<p>　　dwr.xml文件告诉DWR哪些服务是要直接向JavaScript代码公开的。注意，已经要求公开Spring bean
ajaxSampleSvc。DWR将自动找到由应用程序设置的SpringApplicationContext。为此，必须使用标准的servlet
过滤器ContextLoaderListener来初始化Spring ApplicationContext。</p>

　　DWR被设置为一个servlet，所以把它的定义添加到web.xml：
<pre class="code">&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br>&lt;!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD <br> Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"&gt;<br><br>&lt;web-app&gt;<br> &lt;display-name&gt;Ajax Examples&lt;/display-name&gt;<br><br> &lt;listener&gt;<br>  &lt;listener-class&gt;<br>      org.springframework.web.context.ContextLoaderListener<br>  &lt;/listener-class&gt;<br> &lt;/listener&gt;<br>	<br> &lt;servlet&gt;<br>  &lt;servlet-name&gt;ajax_sample&lt;/servlet-name&gt;<br>  &lt;servlet-class&gt;com.tearesolutions.web.AjaxSampleServlet&lt;/servlet-class&gt;<br>  &lt;load-on-startup&gt;1&lt;/load-on-startup&gt;<br> &lt;/servlet&gt;<br><br> &lt;servlet&gt;<br>  &lt;servlet-name&gt;dwr-invoker&lt;/servlet-name&gt;<br>  &lt;display-name&gt;DWR Servlet&lt;/display-name&gt;<br>  &lt;description&gt;Direct Web Remoter Servlet&lt;/description&gt;<br>  &lt;servlet-class&gt;uk.ltd.getahead.dwr.DWRServlet&lt;/servlet-class&gt;<br>  &lt;init-param&gt;<br>   &lt;param-name&gt;debug&lt;/param-name&gt;<br>   &lt;param-value&gt;true&lt;/param-value&gt;<br>  &lt;/init-param&gt;<br> &lt;/servlet&gt;<br><br> &lt;servlet-mapping&gt;<br>  &lt;servlet-name&gt;ajax_sample&lt;/servlet-name&gt;<br>  &lt;url-pattern&gt;/ajax_sample&lt;/url-pattern&gt;<br> &lt;/servlet-mapping&gt;<br><br> &lt;servlet-mapping&gt;<br>  &lt;servlet-name&gt;dwr-invoker&lt;/servlet-name&gt;<br>  &lt;url-pattern&gt;/dwr/*&lt;/url-pattern&gt;<br> &lt;/servlet-mapping&gt;<br>&lt;/web-app&gt;<br></pre>

<p> 　　做完这些之后，可以加载<a href="http://localhost:7001/ajax-demo/dwr" target="_blank">http://localhost:7001/ajax-demo/dwr</a>，看看哪些服务可用。结果如下：</p>

<p><img src="http://dev2dev.bea.com.cn/images/051101/0511010103.jpg" height="102" width="305"></p>

<p>图3. 可用的服务</p>

　　单击ajaxSampleSvc链接，查看有关如何在HTML页面内直接使用服务的示例实现。其中包含的两个JavaScript文件完成了大部分的功能：
<pre class="code">&lt;script type='text/javascript' <br>   src='/ajax-demo/dwr/interface/ajaxSampleSvc.js'&gt;&lt;/script&gt;<br>&lt;script type='text/javascript' <br>   src='/ajax-demo/dwr/engine.js'&gt;&lt;/script&gt;<br></pre>

<p>ajaxSampleSvc.js是动态生成的：</p>

<pre class="code">function ajaxSampleSvc() { }<br><br>ajaxSampleSvc.castVote = function(callback, p0)<br>{ <br>  DWREngine._execute(callback, '/ajax-demo/dwr', <br> 'ajaxSampleSvc', 'castVote', p0);<br>}<br></pre>

<p>　　现在可以使用JavaScript对象ajaxSampleSvc替换所有的XmlHttpRequest代码，从而重构raw-ajax.html文件。可以在dwr-ajax.html文件中看到改动的结果；下面是新的JavaScript函数：</p>

<pre class="code">function castVote(rank) {<br>  ajaxSampleSvc.castVote(processResponse, rank);<br>}<br>function processResponse(data) {<br> var voteText = "<p><strong>Thanks for Voting!</strong></p>"<br>    + "<p>Current ranking: " + data.voteAverage <br>    + " out of 5</p>" <br>    + "<p>Number of votes placed: " <br>    + data.numberOfVotes + "</p>";<br> 502 502'votes').innerHTML = voteText;       <br>}<br></pre>

<p>　　惊人地简单，不是吗？由ajaxSampleSvc对象返回的Article域对象序列化为一个JavaScript对象，允许在它上面调用诸
如numberOfVotes()和voteAverage()之类的方法。在动态生成并插入到DIV元素“votes”中的HTML代码内使用这些数
据。</p>

<p><strong>下一步工作</strong></p>

<p>　　　在后续文章中，我将继续有关Ajax的话题，涉及下面这些方面：</p>

<ul>
<li>Ajax最佳实践</li>
</ul>

<p>　　像许多技术一样，Ajax是一把双刃剑。对于一些用例，其应用程序其实没有必要使用Ajax，使用了反而有损可用性。我将介绍一些不适合使用的模式，突出说明Ajax的一些消极方面，并展示一些有助于缓和这些消极方面的机制。例如，对<a href="http://www.netflix.com/BrowseSelection">Netflix电影浏览器</a>来说，Ajax是合适的解决方案吗？或者，如何提示用户确实出了一些问题，而再次单击按钮也无济于事？</p>

<ul>
<li>管理跨请求的状态</li>
</ul>

<p>　　在使用Ajax时，最初的文档DOM会发生一些变化，并且有大量的页面状态信息存储在客户端变量中。当用户跟踪一个链接到应用程序中的另一个页面时，状态就丢失了。当用户按照惯例单击Back按钮时，呈现给他们的是缓存中的初始页面。这会使用户感到非常迷惑！</p>

<ul>
<li>调试技巧</li>
</ul>

<p>　　使用JavaScript在客户端执行更多的工作时，如果事情不按预期方式进行，就需要一些调试工具来帮助弄清出现了什么问题。</p>

<p><strong>结束语</strong></p>

<p>　　本文介绍了Ajax方法，并展示了如何使用它来创建一个动态且响应灵敏的Web应用程序。通过使用DWR框架，可以轻松地把Ajax融合到站点中，而无需担心所有必须执行的实际管道工作。</p>

<p>　　特别感谢Getahead IT咨询公司的Joe Walker和他的团队开发出DWR这样神奇的工具。感谢你们与世界共享它！</p>

<p><strong>下载</strong></p>

<p>　　本文中演示的应用程序源代码可供下载：<a href="http://dev2dev.bea.com/2005/08/ajax-demo.war" target="_blank">ajax-demo.war</a>（1.52 MB）。</p>

<p><strong>参考资料</strong></p>

<ul>
<li><a href="http://www.getahead.ltd.uk/dwr" target="_blank">http://www.getahead.ltd.uk/dwr</a>——Getahead IT咨询公司。</li><li>Jesse James Garrett所撰写的“<a href="http://www.adaptivepath.com/publications/essays/archives/000385.php" target="_blank">Ajax: A New Approach to Web Applications</a>”（Adaptive Path，2005年二月）。</li><li>“<a href="http://developer.apple.com/internet/webcontent/xmlhttpreq.html" target="_blank">Dynamic HTML and XML: The XMLHttpRequest Object</a>”（Apple Developer Connection）。</li>
</ul>

<p><strong>原文出处</strong></p>

<p>An Introduction To Ajax</p>

<p><a href="http://dev2dev.bea.com/pub/a/2005/08/ajax_introduction.html" target="_blank">http://dev2dev.bea.com/pub/a/2005/08/ajax_introduction.html</a>
</p>
<br>
<img src ="http://www.blogjava.net/ericwang/aggbug/21114.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ericwang/" target="_blank">Dion</a> 2005-11-23 12:53 <a href="http://www.blogjava.net/ericwang/archive/2005/11/23/21114.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在Java2平台企业版中应用异步JavaScript技术和XML（AJAX）</title><link>http://www.blogjava.net/ericwang/archive/2005/11/23/21108.html</link><dc:creator>Dion</dc:creator><author>Dion</author><pubDate>Wed, 23 Nov 2005 03:43:00 GMT</pubDate><guid>http://www.blogjava.net/ericwang/archive/2005/11/23/21108.html</guid><wfw:comment>http://www.blogjava.net/ericwang/comments/21108.html</wfw:comment><comments>http://www.blogjava.net/ericwang/archive/2005/11/23/21108.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/ericwang/comments/commentRss/21108.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ericwang/services/trackbacks/21108.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 在Java2平台企业版中应用异步JavaScript技术和XML（AJAX）	                                                                                                                        ...&nbsp;&nbsp;<a href='http://www.blogjava.net/ericwang/archive/2005/11/23/21108.html'>阅读全文</a><img src ="http://www.blogjava.net/ericwang/aggbug/21108.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ericwang/" target="_blank">Dion</a> 2005-11-23 11:43 <a href="http://www.blogjava.net/ericwang/archive/2005/11/23/21108.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>