| 作者: Chris Duckett, Builder AU 2006-08-22 02:55 PM | ||
|
盡管AJAX是個(gè)相對新生的術(shù)語(yǔ),但其背后的技術(shù)卻并不新穎。隨著(zhù)XMLHttpRequest對象在大多數瀏覽器中的實(shí)現,以及GMail和Google Maps的出現激發(fā)了軟件開(kāi)發(fā)者重新考慮如何構建網(wǎng)頁(yè)的靈感。我們從基礎開(kāi)始認識AJAX,一種顛覆性的技術(shù),其改變了Web應用程序的開(kāi)發(fā)和應用方式,使得交互性頁(yè)面和網(wǎng)站處于目前網(wǎng)絡(luò )潮流的最前沿。 當請求被發(fā)送到瀏覽器后,其所具有的改變網(wǎng)頁(yè)內容的功能已經(jīng)存在許多年了-使用javascript腳本改變iframe‘s src屬性是技術(shù)之一。 XMLHttpRequest對象 XMLHttpRequest對象使AJAX的出現成為可能,它產(chǎn)生異步請求,并決定如何處理結果。在大多數瀏覽器中,我們使用如下代碼來(lái)創(chuàng )建對象: var xmlhttp = false; try { xmlhttp = new XMLHttpRequest(); } catch (e) { alert("cannot create object"); } 不幸的是,上文中所提到的“大多數瀏覽器”并不包括IE,因此對于微軟的瀏覽器,我們需要采用一種特殊的方法創(chuàng )建對象。值得注意的是,根據MSXML解析器的不同版本,需要分別編寫(xiě)兩種代碼。 var xmlhttp = false; try { xmlhttp = new ActiveXObject("Msxml2.XMLHTTP"); } catch (othermicrosoft) { try { xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); } catch (failed) { xmlhttp = false; } 因此,結合上述兩個(gè)代碼片段,我們得到了以下適合所有主流瀏覽器創(chuàng )建XMLHttpRequest對象的代碼: var xmlhttp = false; try { xmlhttp = new XMLHttpRequest(); } catch (trymicrosoft) { try { xmlhttp = new ActiveXObject("Msxml2.XMLHTTP"); } catch (othermicrosoft) { try { xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); } catch (failed) { xmlhttp = false; } } } |
創(chuàng )建時(shí)間選擇
既然我們已經(jīng)知道如何創(chuàng )建XMLHttpRequest,那么還需要考慮何時(shí)來(lái)創(chuàng )建。在XMLHttpRequest對象創(chuàng )建的示例中,盡管我們事先沒(méi)有發(fā)送任何請求,但仍可以創(chuàng )建成功,也就是說(shuō),我們可以在需要的時(shí)候創(chuàng )建對象。
當你需要創(chuàng )建XMLHttpRequest對象時(shí),面對的一個(gè)問(wèn)題是沒(méi)有信息顯示客戶(hù)能否創(chuàng )建該對象。假設一個(gè)用戶(hù)訪(fǎng)問(wèn)你的網(wǎng)站并且無(wú)法創(chuàng )建XMLHttpRequest對象,如果能從第一時(shí)間就知道AJAX沒(méi)有被選擇,你就可以更早地提供給他們。
對于一個(gè)開(kāi)始與你的Web應用程序進(jìn)行互動(dòng)的用戶(hù),當他們被告知只有點(diǎn)擊提交按鈕才能使用該程序時(shí),這決不是一件好事,讓他們填寫(xiě)表單也是完全在浪費時(shí)間。
然而如果在頁(yè)面打開(kāi)階段就能試圖創(chuàng )建XMLHttpRequest,在用戶(hù)開(kāi)始互動(dòng)之前就提供選擇,比如轉入非AJAX頁(yè)面。
設置請求
眼下,有一個(gè)頁(yè)面在加載后就需要創(chuàng )建XMLHttpRequest對象,我們現在有意做出請求。
為此,我們必須向打開(kāi)方式傳遞至少兩個(gè)參數,其中大部分參數的含義都可以直接理解。
xmlhttp.open(Method, Url [, Async] [, User] [, Password])
參數Method定義了請求方法;可選擇的有"POST", "GET" 或 "HEAD"。首先,我們只準備使用GET。
Url是要請求頁(yè)面的url字符串。我們無(wú)法訪(fǎng)問(wèn)和向舊頁(yè)面做出請求,有一個(gè)阻止我們訪(fǎng)問(wèn)與所創(chuàng )建請求頁(yè)面不在同一范圍內的其他頁(yè)面的壁壘。
Async也是AJAX建立的基礎。盡管前者在A(yíng)PI中是一個(gè)備選參數,但在這里該參數是強制性選項,必須設置為true,如果設為false,該項將保持直到獲得響應。該參數的默認值是True,所以你可以選擇不設置,但是考慮到可讀性以及可維護性,強烈推薦設置該值。
用戶(hù)名和密碼用于驗證身份時(shí)選擇
一旦我們設置Async為true,我們需要定義一種命令方式,用于回應請求狀態(tài)改變-可以通過(guò)設置XMLHttpRequest對象中的onreadystatechange屬性完成。
xmlhttp.onreadystatechange = myReturnMethod;
xmlhttp.send(null);
最后需要做的是把請求發(fā)送出去,先說(shuō)最簡(jiǎn)單的,我們只發(fā)送不含任何附加數據的請求:
在一個(gè)簡(jiǎn)單的示例中綜合了上述所有內容:
xmlhttp.open("GET", "/some_dir/myfile.html", true);
xmlhttp.onreadystatechange = myReturnMethod;
xmlhttp.send(null);
上述三行代碼就是我們進(jìn)入AJAX世界所需要的全部?jì)热?,沒(méi)有包含任何復雜的概念或曲折的邏輯
對象狀態(tài)
上文中提到過(guò),我們需要定義一種命令方式,用于回應請求狀態(tài)改變,這可以通過(guò)設置onreadystatechange屬性完成,請注意我所說(shuō)的是“狀態(tài)改變”,并不是“完成”-因為這兩者之間是完全不同的。結合我們剛才所學(xué)的知識,最好通過(guò)例子來(lái)說(shuō)明。寫(xiě)入如下代碼:
<script>
var xmlhttp = false;
try {
xmlhttp = new XMLHttpRequest();
} catch (trymicrosoft) {
try {
xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch (othermicrosoft) {
try {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (failed) {
xmlhttp = false;
}
}
}
function goAJAX() {
alert("InitialReadyState is:"+xmlhttp.readyState);
xmlhttp.onreadystatechange = myReturnMethod;
xmlhttp.open("GET", "another_file.html", true);
xmlhttp.send(null);
}
function myReturnMethod() {
alert("NewReadyState is:"+xmlhttp.readyState);
}
</script>
<button onclick="javascript:goAJAX()">Let‘s try it!</button>
在被請求的文件中,我們可以填補任何我們想要的內容,只要可以建立,我們就不打算使用其中的內容。
點(diǎn)提交按鈕前,我們還應該注意,對象狀態(tài)警告出現多次,最終顯示的對象狀態(tài)值是4。不同的瀏覽器對此的處理方法不同,Safari精確地從0-4計數,對于firefox,1出現兩次,0只在運行的第一次出現。值得慶幸的是,我們僅僅對readyState是4的情況感興趣,但是為了完整性,我們還是看看其他數字所代表的含義。
0:未初始化-打開(kāi)命令之前,對象不包含數據
1:對象正在加載數據-發(fā)送請求命令之前
2:加載完畢-請求已經(jīng)被發(fā)出
3:交互-請求正在被處理,可能會(huì )得到一些數據,但不完全,所以不能安全使用
4:完成-請求已完成,最終數據可以安全使用。
這些新信息說(shuō)明,只有在警告顯示請求完成后,才能改變myReturnMethod。
function myReturnMethod() {
if (xmlhttp.readyState==4){
alert("NewReadyState is:"+xmlhttp.readyState);
}
響應狀況
采用先前的代碼,我們改變open method中的url參數到一個(gè)不存在的文件中:
xmlhttp.open("GET", "filethatdoesnotexist.html", true);
再次運行該代碼,我們看到和先前的代碼執行了相同的結果-為什么會(huì )這樣?
如果這是一個(gè)正常http請求,我們預計404錯誤會(huì )出現-在A(yíng)JAX中也具有相同的狀況屬性。該屬性?xún)H可讀,包含HTTP請求狀況代碼,我們這里的文件對應的代碼是404。應用于一般http請求的相同狀況代碼仍適用AJAX。(注意:為了正常運行,頁(yè)面應該讀取來(lái)自一個(gè)HTTP服務(wù)器的請求,如果從文件系統中讀取,該狀況代碼將始終是未定義的。)
因此,我們需要增加一個(gè)if陳述,用來(lái)測試請求是否成功,如果是一個(gè)200的響應代碼,將被成功返回。如果不成功,我們必須選擇恰當的方式來(lái)解決錯誤。
function myReturnMethod() {
if (xmlhttp.readyState==4){
if (xmlhttp.status == 200) {
alert("Request successfully completed");
}
else if(xmlhttp.status == 404) {
alert("Requested file not found");
}
else {
alert("Error has occurred with status code:
"+xmlhttp.status);
}
}
}
ResponseText與responseXML
盡管我們已經(jīng)可以成功地完成一個(gè)AJAX請求,但還需要對返回的數據做出處理。通過(guò)應用responseText 或responseXML屬性,我們有兩種處理的方式。最簡(jiǎn)單的方法是直接獲取從服務(wù)器返回的數據,使用responseText屬性中返回的純文本數據。這樣得到的數據可以是任何我們想要的形式;簡(jiǎn)單的文本響應,符號劃界值或一整本字符串名冊。在我們的返回方法中,我們將在警告中輸出responseText,這將輸出我們請求的整個(gè)文件內容。
在這個(gè)階段,如果你還沒(méi)有向我們發(fā)出請求的文件- another_file.html中輸入任何內容,你應該做了。
function myReturnMethod() {
if (xmlhttp.readyState==4){
if (xmlhttp.status == 200) {
alert(xmlhttp.responseText);
}
else if(xmlhttp.status == 404) {
alert("Requested file not found");
}
else {
alert("Error has occurred with status code:
"+xmlhttp.status);
}
}
}
另一個(gè)可供選擇的找回返回數據的方法是使用responseXML屬性,其將以XML文檔對象的形式返回數據,利用Javascript的DOM功能可以對其研究。為了看到這一動(dòng)作的結果,我們需要返回一個(gè)恰當的XML格式文檔;我們就創(chuàng )建一個(gè)新文件來(lái)請求(xmlresult.html), 并寫(xiě)入以下代碼:
<?xml version="1.0" encoding="UTF-8"?>
<root>
XML Formatted Result
</root>
然后我們需要將打開(kāi)命令改為:
xmlhttp.open("GET", "xmlresult.html", true);
在我們的返回方法中所作的最大改變是,我們需要在那里替換警告(xmlhttp.responseText),代碼如下:
xmldoc = xmlhttp.responseXML;
rootnode = xmldoc.getElementsByTagName(‘root‘).item(0);
alert(rootnode.firstChild.data);
瞧,我們正在利用DOM來(lái)返回數據。
通過(guò)結合我們所了解到的現有的javascript功能,該數據可被用于應付一個(gè)頁(yè)面的內容。例如:
document.getElementById(‘resultdiv‘).innerHTML = xmlhttp.responseText;
何時(shí)使用AJAX
既然已經(jīng)知道如何創(chuàng )建一個(gè)請求以及返回結果,那么還有一個(gè)問(wèn)題是何時(shí)該使用AJAX呢?
對于一項新技術(shù)自然會(huì )有兩方面的極端意見(jiàn),一些網(wǎng)絡(luò )開(kāi)發(fā)者認為,如果沒(méi)有AJAX,生活將變得更簡(jiǎn)單;另一些人則巴不得見(jiàn)到由AJAX構建的整個(gè)網(wǎng)站。通常這種情況下,真理往往介于這兩種觀(guān)點(diǎn)之間。當決定在你的網(wǎng)站上使用AJAX時(shí),你也應當考慮其中帶來(lái)的后果。首先,所有應用了AJAX的頁(yè)面無(wú)法加入書(shū)簽。隨著(zhù)AJAX的使用,也為你的網(wǎng)站引入了一種“狀態(tài)”,而除了初始頁(yè)面外,用戶(hù)無(wú)法返回到任何其他頁(yè)面。
原因是在用戶(hù)的瀏覽器上url沒(méi)有改變-如果其發(fā)生改變,AJAX就沒(méi)有存在的必要了。
其次,如果想要支持每一位訪(fǎng)問(wèn)網(wǎng)站的用戶(hù),就需要付出雙倍的工作。對于你創(chuàng )建使用AJAX網(wǎng)站的每個(gè)片段,你都必須迎合沒(méi)有javascript功能用戶(hù)的需要。如果為了javascript和非javascript用戶(hù)具有相同的頁(yè)面級別,需要做大量額外的工作,必須使用noscript標簽。也就是說(shuō),AJAX通過(guò)減少瀏覽和交互時(shí)所需要的頁(yè)面重載次數,從而提高了網(wǎng)站的可用性。結合javascript效果庫,我們可以創(chuàng )建一些非常引人注目的頁(yè)面交互功能,這在兩年前是不可能實(shí)現的。因此,我的觀(guān)點(diǎn)是在你的網(wǎng)站的非主要位置使用AJAX。例如,在某個(gè)網(wǎng)站,如果將AJAX結構應用到文章中將是極其錯誤的想法,因為你再也無(wú)法將未讀完的文章加入書(shū)簽。另一方面,一種文章評論或對話(huà)系統則非常適合應用AJAX。
責任編輯:張琎
聯(lián)系客服