2.3 Prototype對(duì)Ajax的支持
作為一個(gè)Ajax開發(fā)框架,Prototype對(duì)Ajax開發(fā)提供了有力的支持。在Prototype中,與Ajax相關(guān)的類和對(duì)象包括:Ajax、Ajax.Responsders、Ajax.Base、Ajax.Request、Ajax. PeriodicalUpdater和Ajax.Updater,圖2-3所示為這些類和對(duì)象之間的關(guān)系及其常用屬性和方法,下面分別對(duì)這些類和對(duì)象進(jìn)行介紹。

圖2-3 Prototype中Ajax相關(guān)類和對(duì)象關(guān)系示意圖
2.3.1 Ajax對(duì)象
Ajax對(duì)象為其他的Ajax功能類提供了最基本的支持,它的實(shí)現(xiàn)如2.2.7節(jié)中例2-10所示,其中包括一個(gè)方法getTransport和一個(gè)屬性activeRequestCount。getTransport方法返回一個(gè)XMLHttpRequest對(duì)象,activeRequestCount屬性代表正在處理中的Ajax請(qǐng)求的個(gè)數(shù)。
2.3.2 Ajax.Base類
Ajax.Base類是Ajax.Request類和Ajax.PeriodicalUpdater類的基類。它提供了3個(gè)方法:
l setOptions:設(shè)置Ajax操作所使用的選項(xiàng)。
l responseIsSuccess:判斷Ajax操作是否成功。
l responseIsFailure:判斷Ajax操作是否失敗(與responseIsSuccess相反)。
Ajax.Base類的主要作用是提取出一些公用的方法,其他類通過(guò)繼承的方式使用這些方法,實(shí)現(xiàn)代碼復(fù)用。
2.3.3 Ajax.Request類
這是Prototype中最經(jīng)常使用的一個(gè)Ajax相關(guān)類。Ajax.Request類的方法通常是內(nèi)部使用的,因此這里就不一一列舉,有興趣的讀者可以參考Prototype的源代碼。這里重點(diǎn)講講如何使用Ajax.Request類,首先給出一個(gè)最簡(jiǎn)單的Ajax.Request類的應(yīng)用示例,如例2-11所示,注意示例中的黑體字。
例2-11 Ajax.Request類應(yīng)用示例
Ajax.Request測(cè)試頁(yè)面:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>chapter 3</title>
<script type="text/javascript" language="javascript"
src="prototype.js" ></script>
<script type="text/javascript" language="javascript">
function test() {
// 創(chuàng)建Ajax.Request對(duì)象,發(fā)起一個(gè)Ajax請(qǐng)求
var myAjax = new Ajax.Request(
'data.html', // 請(qǐng)求的URL
{
method: 'get', // 使用GET方式發(fā)送HTTP請(qǐng)求
onComplete: showResponse // 指定請(qǐng)求成功完成時(shí)需要執(zhí)行的方法
}
);
}
function showResponse(response) {
$('divResult').innerHTML = response.responseText;
}
</script>
</head>
<body>
<input type="button" value="click" onclick="test()" />
<div id="divResult" />
</body>
</html>
data.html:
<input type="text" id="name" />
<input type="button" value="Click Me" onclick="sayHi()">
Ajax.Request對(duì)象在初始化時(shí)需要提供兩個(gè)參數(shù):第1個(gè)參數(shù)是將要請(qǐng)求頁(yè)面的URL,這里使用的data.html是一個(gè)普通的HTML靜態(tài)頁(yè)面;第2個(gè)參數(shù)是Ajax操作的選項(xiàng),在Prototype中并沒(méi)有專門為Ajax操作選項(xiàng)定義一個(gè)類,通常都是像例2-11這樣,通過(guò)匿名對(duì)象的方式設(shè)置Ajax操作的參數(shù)。在例2-11中,Ajax操作選項(xiàng)具有兩個(gè)屬性:method表示HTTP請(qǐng)求方式,默認(rèn)是POST方式;onComplete指定了Ajax操作完成以后(即XMLHttpRequest對(duì)象的status屬性為4時(shí)),頁(yè)面將要執(zhí)行的函數(shù)。當(dāng)然,Ajax操作還包括很多其他選項(xiàng),如表2-1所示。
表2-1 Ajax操作選項(xiàng)屬性含義
屬性名稱
|
含義
|
method
|
HTTP請(qǐng)求方式(POST/GET/HEAD)。
|
parameters
|
在HTTP請(qǐng)求中傳入的URL格式的值列表,即URL串中問(wèn)號(hào)之后的部分。
|
asynchronous
|
是否做異步XMLHttpRequest請(qǐng)求。
|
postBody
|
在POST請(qǐng)求方式下,傳入請(qǐng)求體中的內(nèi)容。
|
requestHeaders
|
和請(qǐng)求一起被傳入的HTTP頭部列表,這個(gè)列表必須含有偶數(shù)個(gè)項(xiàng)目,因?yàn)榱斜碇忻績(jī)身?xiàng)為一組,分別代表自定義部分的名稱和與之對(duì)應(yīng)的字符串值。
|
onXXXXXXXX
|
在HTTP請(qǐng)求、響應(yīng)的過(guò)程中,當(dāng)XMLHttpRequest對(duì)象狀態(tài)發(fā)生變化時(shí)調(diào)用的響應(yīng)函數(shù)。響應(yīng)函數(shù)有5個(gè):onUninitialized、onLoading、onLoaded、onInteractive和onComplete。傳入這些函數(shù)的參數(shù)可以有2個(gè),其中第1個(gè)參數(shù)是執(zhí)行HTTP請(qǐng)求的XMLHttpRequest對(duì)象,第2個(gè)參數(shù)是包含被執(zhí)行的X-JSON響應(yīng)的HTTP頭。
|
onSuccess
|
Ajax操作成功完成時(shí)調(diào)用的響應(yīng)函數(shù),傳入的參數(shù)與onXXXXXXXX相同。
|
onFailure
|
Ajax操作請(qǐng)求完成但出現(xiàn)錯(cuò)誤時(shí)調(diào)用的響應(yīng)函數(shù),傳入的參數(shù)與onXXXXXXXX相同。
|
onException
|
Ajax操作發(fā)生異常情況時(shí)調(diào)用的響應(yīng)函數(shù),它可以接收2個(gè)參數(shù),其中第1個(gè)參數(shù)是執(zhí)行HTTP請(qǐng)求的XMLHttpRequest對(duì)象,第2個(gè)參數(shù)是異常對(duì)象。
|
2.3.4 Ajax.Updater類
例2-11使用Ajax.Request類實(shí)現(xiàn)了頁(yè)面的局部刷新效果,而這樣類似的功能在Ajax應(yīng)用中是經(jīng)常使用的。因此,為了簡(jiǎn)化這種工作,Prototype框架從Ajax.Requet類中派生出一個(gè)子類——Ajax.Updater。與Ajax.Request相比,Ajax.Updater的初始化多了一個(gè)container參數(shù),該參數(shù)代表將要更新的頁(yè)面元素的id。例2-11的功能通過(guò)Ajax.Updater的實(shí)現(xiàn),會(huì)變得更加簡(jiǎn)單,如例2-12所示。
例2-12 Ajax.Updater類的應(yīng)用示例
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>chapter 3</title>
<script type="text/javascript" language="javascript"
src="prototype.js" ></script>
<script type="text/javascript" language="javascript">
function test() {
var myAjax = new Ajax.Updater(
'divResult', // 更新的頁(yè)面元素
'data.html', // 請(qǐng)求的URL
{
method: 'get'
}
);
}
</script>
</head>
<body>
<input type="button" value="click" onclick="test()" />
<div id="divResult" />
</body>
</html>
此外,Ajax.Updater類還有另外一個(gè)功能,如果請(qǐng)求的頁(yè)面內(nèi)容中包括JavaScript腳本,Ajax.Updater類可以執(zhí)行其中的腳本,只需要在Ajax操作選項(xiàng)中增加屬性“evalScripts: true”即可。對(duì)例2-11中的data.html進(jìn)行修改,在其中加入JavaScript腳本,如例2-13所示。
例2-13 data.html
<script language="javascript" type="text/javascript">
sayHi = function() {
alert("Hello, " + $F('name') + "!");
}
</script>
<input type="text" id="name" />
<input type="button" value="Click Me" onclick="sayHi()">
調(diào)用Ajax.Updater的JavaScript腳本修改為:
function test() {
var myAjax = new Ajax.Updater(
'divResult', // 更新的頁(yè)面元素
'data.html', // 請(qǐng)求的URL
{
method: 'get',
evalScripts: true
}
);
}
這樣就可以使用data.html頁(yè)面的內(nèi)容更新當(dāng)前頁(yè)面中的<div>元素divResult,并且執(zhí)行data.html頁(yè)面中包含的JavaScript腳本。
這里需要注意的是例2-13中sayHi函數(shù)的寫法,如果寫成
function sayHi() {
alert("Hello, " + $F('name') + "!");
}
或者
var sayHi = function() {
alert("Hello, " + $F('name') + "!");
}
程序是不能正常運(yùn)行的。這是因?yàn)锳jax.Updater執(zhí)行腳本是通過(guò)eval的方式,而不是將腳本內(nèi)容引入到當(dāng)前頁(yè)面,直接聲明的function sayHi或者用var聲明的sayHi函數(shù),其作用域只是在這段腳本內(nèi)部,外部的其他腳本不能訪問(wèn)sayHi函數(shù)。而按照例2-13的方式聲明的函數(shù),其作用域是整個(gè)window。
2.3.5 Ajax.PeriodicalUpdater類
和Ajax.Request類相似,Ajax.PeriodicalUpdater類也繼承自Ajax.Base類。在一些Ajax應(yīng)用中,需要周期性地更新某些頁(yè)面元素,例如天氣預(yù)報(bào)、即時(shí)新聞等等。實(shí)現(xiàn)這樣的功能通常要使用JavaScript中的定時(shí)器函數(shù)setTimeout、clearTimeout等,而有了Ajax.PeriodicalUpdater類可以很好地簡(jiǎn)化這類編碼工作。
新建一個(gè)Ajax. PeriodicalUpdater類的實(shí)例需要指定3個(gè)參數(shù):
l container:將要更新的頁(yè)面元素id;
l url:請(qǐng)求的URL地址;
l options:Ajax操作選項(xiàng)。
和Ajax.Updater類相似,Ajax.PeriodicalUpdater類也支持動(dòng)態(tài)執(zhí)行JavaScript腳本,只需在Ajax操作選項(xiàng)中增加(evalScripts: true)屬性值即可。
Ajax.PeriodicalUpdater類支持兩個(gè)特殊的Ajax操作選項(xiàng):frequency和decay。frequency參數(shù)很容易理解,既然是定時(shí)更新頁(yè)面元素,或者定時(shí)執(zhí)行腳本,那么多長(zhǎng)時(shí)間更新或者執(zhí)行一次呢?frequency指的就是兩次Ajax操作之間的時(shí)間間隔,單位是秒,默認(rèn)值為2秒。
如果僅指定frequency參數(shù),程序會(huì)按照固定的時(shí)間間隔執(zhí)行Ajax操作。這樣的更新策略合理嗎?答案取決于請(qǐng)求URL中數(shù)據(jù)的更新頻率。如果請(qǐng)求的數(shù)據(jù)會(huì)很有規(guī)律地按照固定頻率改變,那么只要設(shè)置一個(gè)合適的frequency值,就可以很有效地實(shí)現(xiàn)頁(yè)面的定時(shí)更新。然而實(shí)際應(yīng)用中的數(shù)據(jù)往往不會(huì)那么理想,例如新聞,可能在一天中只有特定的一段時(shí)間更新頻率會(huì)很高,而在其他時(shí)間則幾乎沒(méi)有變化。經(jīng)常遇到這樣的情況該怎么辦呢?Ajax.PeriodicalUpdater類支持的decay屬性就是為了解決這個(gè)問(wèn)題而產(chǎn)生的。當(dāng)option中帶有decay屬性時(shí),如果請(qǐng)求返回的數(shù)據(jù)與上次相同,那么下次進(jìn)行Ajax操作的時(shí)間間隔會(huì)乘以一個(gè)decay的系數(shù)。
為了比較明顯地看到decay屬性的效果,在請(qǐng)求的測(cè)試頁(yè)面中加入記錄時(shí)間的腳本,代碼如例2-14所示。
例2-14 Ajax.PeriodicalUpdater類應(yīng)用示例
ex10.html:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>chapter 3</title>
<script type="text/javascript" language="javascript"
src="prototype.js" ></script>
<script type="text/javascript" language="javascript">
var str='';
var intcount=0;
function test() {
var myAjax = new Ajax.PeriodicalUpdater(
'divResult', // 定時(shí)更新的頁(yè)面元素
'script1.html', // 請(qǐng)求的URL
{
method: 'get', // HTTP請(qǐng)求的方式為GET
evalScripts: true, // 是否執(zhí)行請(qǐng)求頁(yè)面中的腳本
frequency: 1, // 更新的頻率
decay: 1 // 衰減系數(shù)
}
);
}
</script>
</head>
<body>
<input type="button" value="click" onclick="test()" />
<div id="divResult" ></div>
<div id="divResult2" ></div>
</body>
</html>
script1.html:
<script language="javascript" type="text/javascript">
// Ajax.PeriodicalUpdater調(diào)用函數(shù)計(jì)數(shù)
// 在<div>元素divResult2中增加一行結(jié)果,并記錄當(dāng)前時(shí)間和
// Ajax.PeriodicalUpdater的調(diào)用次數(shù)
intcount++;
str = $('divResult2').innerHTML;
$('divResult2').innerHTML = str + "count = " + intcount+ ": " + new Date() + "<br>";
</script>
例2-14的運(yùn)行結(jié)果如圖2-4所示。

圖2-4 Ajax.PeriodicalUpdater類應(yīng)用示例
可以看到,由于請(qǐng)求返回的數(shù)據(jù)一直沒(méi)有發(fā)生變化,每次請(qǐng)求時(shí)間的間隔是上一次的2倍(decay=2)。如果某一次請(qǐng)求返回的數(shù)據(jù)發(fā)生了變化,那么執(zhí)行請(qǐng)求的時(shí)間間隔則恢復(fù)到初始值。
2.3.6 Ajax.Responders對(duì)象
Ajax.Responders對(duì)象維護(hù)了一個(gè)正在運(yùn)行的Ajax對(duì)象列表,在需要實(shí)現(xiàn)一些全局的功能時(shí)就可以使用它。例如,在Ajax請(qǐng)求發(fā)出以后需要提示用戶操作正在執(zhí)行中,而操作返回以后則取消提示。利用Ajax.Responders對(duì)象就可以實(shí)現(xiàn)這樣的功能,如例2-15所示。
例2-15 Ajax.Responders對(duì)象應(yīng)用示例
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>chapter 3</title>
<script type="text/javascript" language="javascript"
src="prototype.js" ></script>
<script type="text/javascript" language="javascript">
function test() {
var myAjax = new Ajax.Request(
'data.html',
{
method: 'get',
onComplete: showResponse
}
);
}
function showResponse(response) {
$('divResult').innerHTML = response.responseText;
}
var handle = {
onCreate: function() {
Element.show('loading'); // 當(dāng)創(chuàng)建Ajax請(qǐng)求時(shí),顯示loading
},
onComplete: function() {
// 當(dāng)請(qǐng)求成功返回時(shí),如果當(dāng)前沒(méi)有其他正在運(yùn)行中的Ajax請(qǐng)求,隱藏loading
if (Ajax.activeRequestCount == 0) {
Element.hide('loading');
}
}
};
// 將handle注冊(cè)到全局的Ajax.Responders對(duì)象中,使其生效
Ajax.Responders.register(handle);
</script>
</head>
<body>
<input type="button" value="click" onclick="test()" />
<div id="divResult" ></div>
<div id='loading' style="display:none">
<img src="loading.gif">Loading...
</div>
</body>
</html>
例2-15中定義了一個(gè)handle對(duì)象,其中包含onCreate和onComplete函數(shù)。頁(yè)面中發(fā)出任何一個(gè)Ajax請(qǐng)求時(shí)都會(huì)調(diào)用onCreate方法,而請(qǐng)求完成時(shí)都會(huì)調(diào)用onComplete方法。例2-15的運(yùn)行結(jié)果如圖2-5所示。