一、使用Ajax的主要原因
1、通過適當(dāng)?shù)腁jax應(yīng)用達到更好的用戶體驗;
2、把以前的一些服務(wù)器負擔(dān)的工作轉(zhuǎn)嫁到客戶端,利于客戶端閑置的處理能力來處理,減輕服務(wù)器和帶寬的負擔(dān),從而達到節(jié)約ISP的空間及帶寬租用成本的目的。
二、引用
Ajax這個概念的最早提出者Jesse James Garrett認為:Ajax是Asynchronous JavaScript and XML的縮寫。Ajax并不是一門新的語言或技術(shù),它實際上是幾項技術(shù)按一定的方式組合在一在同共的協(xié)作中發(fā)揮各自的作用,它包括:
·使用XHTML和CSS標(biāo)準(zhǔn)化呈現(xiàn);
·使用DOM實現(xiàn)動態(tài)顯示和交互;
·使用XML和XSLT進行數(shù)據(jù)交換與處理;
·使用XMLHttpRequest進行異步數(shù)據(jù)讀取;
·最后用JavaScript綁定和處理所有數(shù)據(jù);
Ajax的工作原理相當(dāng)于在用戶和服務(wù)器之間加了—個中間層,使用戶操作與服務(wù)器響應(yīng)異步化。并不是所有的用戶請求都提交給服務(wù)器,像—些數(shù)據(jù)驗證和數(shù)據(jù)處理等都交給Ajax引擎自己來做,只有確定需要從服務(wù)器讀取新數(shù)據(jù)時再由Ajax引擎代為向服務(wù)器提交請求。
圖2-1
圖2-2
三、概述
雖然Garrent列出了7條Ajax的構(gòu)成技術(shù),但個人認為,所謂的Ajax其核心只有JavaScript、XMLHTTPRequest和DOM,如果所用數(shù)據(jù)格式為XML的話,還可以再加上XML這一項(Ajax從服務(wù)器端返回的數(shù)據(jù)可以是XML格式,也可以是文本等其他格式)。
在舊的交互方式中,由用戶觸發(fā)一個HTTP請求到服務(wù)器,服務(wù)器對其進行處理后再返回一個新的HTHL頁到客戶端,每當(dāng)服務(wù)器處理客戶端提交的請求時,客戶都只能空閑等待,并且哪怕只是一次很小的交互、只需從服務(wù)器端得到很簡單的一個數(shù)據(jù),都要返回一個完整的HTML頁,而用戶每次都要浪費時間和帶寬去重新讀取整個頁面。
而使用Ajax后用戶從感覺上幾乎所有的操作都會很快響應(yīng)沒有頁面重載(白屏)的等待。
1、XMLHTTPRequest
Ajax的一個最大的特點是無需刷新頁面便可向服務(wù)器傳輸或讀寫數(shù)據(jù)(又稱無刷新更新頁面),這一特點主要得益于XMLHTTP組件XMLHTTPRequest對象。這樣就可以向再發(fā)桌面應(yīng)用程序只同服務(wù)器進行數(shù)據(jù)層面的交換,而不用每次都刷新界面也不用每次將數(shù)據(jù)處理的工作提交給服務(wù)器來做,這樣即減輕了服務(wù)器的負擔(dān)又加快了響應(yīng)速度、縮短了用戶等候時間。
最早應(yīng)用XMLHTTP的是微軟,IE(IE5以上)通過允許開發(fā)人員在Web頁面內(nèi)部使用XMLHTTP ActiveX組件擴展自身的功能,開發(fā)人員可以不用從當(dāng)前的Web頁面導(dǎo)航而直接傳輸數(shù)據(jù)到服務(wù)器上或者從服務(wù)器取數(shù)據(jù)。這個功能是很重要的,因為它幫助減少了無狀態(tài)連接的痛苦,它還可以排除下載冗余HTML的需要,從而提高進程的速度。Mozilla(Mozilla1.0以上及NetScape7以上)做出的回應(yīng)是創(chuàng)建它自己的繼承XML代理類:XMLHttpRequest類。Konqueror (和Safari v1.2,同樣也是基于KHTML的瀏覽器)也支持XMLHttpRequest對象,而Opera也將在其v7.6x+以后的版本中支持XMLHttpRequest對象。對于大多數(shù)情況,XMLHttpRequest對象和XMLHTTP組件很相似,方法和屬性也類似,只是有一小部分屬性不支持。
XMLHttpRequest的應(yīng)用:
·XMLHttpRequest對象在JS中的應(yīng)用
var xmlhttp = new XMLHttpRequest();
·微軟的XMLHTTP組件在JS中的應(yīng)用
var xmlhttp = new ActiveXObject(Microsoft.XMLHTTP);
var xmlhttp = new ActiveXObject(Msxml2.XMLHTTP);
XMLHttpRequest 對象方法
/**
* Cross-browser XMLHttpRequest instantiation.
*/
if (typeof XMLHttpRequest == ’undefined’) {
XMLHttpRequest = function () {
var msxmls = [’MSXML3’, ’MSXML2’, ’Microsoft’]
for (var i=0; i < msxmls.length; i++) {
try {
return new ActiveXObject(msxmls[i]+’.XMLHTTP’)
} catch (e) { }
}
throw new Error("No XML component installed!")
}
}
function createXMLHttpRequest() {
try {
// Attempt to create it "the Mozilla way"
if (window.XMLHttpRequest) {
return new XMLHttpRequest();
}
// Guess not - now the IE way
if (window.ActiveXObject) {
return new ActiveXObject(getXMLPrefix() + ".XmlHttp");
}
}
catch (ex) {}
return false;
};
XMLHttpRequest 對象方法
?方法 | ?描述 |
?abort() | ?停止當(dāng)前請求 |
?getAllResponseHeaders() | ?作為字符串返問完整的headers |
?getResponseHeader("headerLabel") | ?作為字符串返問單個的header標(biāo)簽 |
?open("method","URL"[,asyncFlag[,"userName"[, "password"]]]) | ?設(shè)置未決的請求的目標(biāo) URL, 方法, 和其他參數(shù) |
?send(content) | ?發(fā)送請求 |
?setRequestHeader("label", "value") | ?設(shè)置header并和請求一起發(fā)送 |
XMLHttpRequest 對象屬性
?屬性 | ?描述 |
?onreadystatechange | ?狀態(tài)改變的事件觸發(fā)器 |
?readyState | ?對象狀態(tài)(integer): ?0 = 未初始化 ?1 = 讀取中 ?2 = 已讀取 ?3 = 交互中 ?4 = 完成 |
?responseText | ?服務(wù)器進程返回數(shù)據(jù)的文本版本 |
?responseXML | ?服務(wù)器進程返回數(shù)據(jù)的兼容DOM的XML文檔對象 |
?status | ?服務(wù)器返回的狀態(tài)碼, 如:404 = "文件末找到" 、200 ="成功" |
?statusText | ?服務(wù)器返回的狀態(tài)文本信息 |
2、JavaScript
JavaScript是一在瀏覽器中大量使用的編程語言,,他以前一直被貶低為一門糟糕的語言(他確實在使用上比較枯燥),以在常被用來作一些用來炫耀的小玩意和惡作劇或是單調(diào)瑣碎的表單驗證。但事實是,他是一門真正的編程語言,有著自已的標(biāo)準(zhǔn)并在各種瀏覽器中被廣泛支持。
3、DOM
Document Object Model。
DOM是給 HTML 和 XML 文件使用的一組 API。它提供了文件的結(jié)構(gòu)表述,讓你可以改變其中的內(nèi)容及可見物。其本質(zhì)是建立網(wǎng)頁與 Script 或程序語言溝通的橋梁。
所有WEB開發(fā)人員可操作及建立文件的屬性、方法及事件都以對象來展現(xiàn)(例如,document 就代表“文件本身“這個對像,table 對象則代表 HTML 的表格對象等等)。這些對象可以由當(dāng)今大多數(shù)的瀏覽器以 Script 來取用。
一個用HTML或XHTML構(gòu)建的網(wǎng)頁也可以看作是一組結(jié)構(gòu)化的數(shù)據(jù),這些數(shù)據(jù)被封在DOM(Document Object Model)中,DOM提供了網(wǎng)頁中各個對象的讀寫的支持。
4、XML
可擴展的標(biāo)記語言(Extensible Markup Language)具有一種開放的、可擴展的、可自描述的語言結(jié)構(gòu),它已經(jīng)成為網(wǎng)上數(shù)據(jù)和文檔傳輸?shù)臉?biāo)準(zhǔn)。它是用來描述數(shù)據(jù)結(jié)構(gòu)的一種語言,就正如他的名字一樣。他使對某些結(jié)構(gòu)化數(shù)據(jù)的定義更加容易,并且可以通過他和其他應(yīng)用程序交換數(shù)據(jù)。
5、綜合
Jesse James Garrett提到的Ajax引擎,實際上是一個比較復(fù)雜的JavaScript應(yīng)用程序,用來處理用戶請求,讀寫服務(wù)器和更改DOM內(nèi)容。
JavaScript的Ajax引擎讀取信息,并且互動地重寫DOM,這使網(wǎng)頁能無縫化重構(gòu),也就是在頁面已經(jīng)下載完畢后改變頁面內(nèi)容,這是我們一直在通過JavaScript和DOM在廣泛使用的方法,但要使網(wǎng)頁真正動態(tài)起來,不僅要內(nèi)部的互動,還需要從外部獲取數(shù)據(jù),在以前,我們是讓用戶來輸入數(shù)據(jù)并通過DOM來改變網(wǎng)頁內(nèi)容的,但現(xiàn)在,XMLHTTPRequest,可以讓我們在不重載頁面的情況下讀寫服務(wù)器上的數(shù)據(jù),使用戶的輸入達到最少。
基于XML的網(wǎng)絡(luò)通訊也并不是新事物,實際上FLASH和JAVA Applet都有不錯的表現(xiàn),現(xiàn)在這種富交互在網(wǎng)頁上也可用了,基于標(biāo)準(zhǔn)化的并被廣泛支持和技術(shù),并且不需要插件或下載小程序。
Ajax是傳統(tǒng)WEB應(yīng)用程序的一個轉(zhuǎn)變。以前是服務(wù)器每次生成HTML頁面并返回給客戶端(瀏覽器)。在大多數(shù)網(wǎng)站中,很多頁面中至少90%都是一樣的,比如:結(jié)構(gòu)、格式、頁頭、頁尾、廣告等,所不同的只是一小部分的內(nèi)容,但每次服務(wù)器都會生成所有的頁面再返回給客戶端,這無形之中是一種浪費,不管是對于用戶的時間、帶寬、CPU耗用,還是對于ISP的高價租用的帶寬和空間來說。如果按一頁來算,只能幾K或是幾十K可能并不起眼,但像SINA每天要生成幾百萬個頁面的大ISP來說,可以說是損失巨大的。而AJAX可以所為客戶端和服務(wù)器的中間層,來處理客戶端的請求,并根據(jù)需要向服務(wù)器端發(fā)送請求,用什么就取什么、用多少就取多少,就不會有數(shù)據(jù)的冗余和浪費,減少了數(shù)據(jù)下載總量,而且更新頁面時不用重載全部內(nèi)容,只更新需要更新的那部分即可,相對于純后臺處理并重載的方式縮短了用戶等待時間,也把對資源的浪費降到最低,基于標(biāo)準(zhǔn)化的并被廣泛支持和技術(shù),并且不需要插件或下載小程序,所以Ajax對于用戶和ISP來說是雙盈的。
Ajax使WEB中的界面與應(yīng)用分離(也可以說是數(shù)據(jù)與呈現(xiàn)分離),而在以前兩者是沒有清晰的界限的,數(shù)據(jù)與呈現(xiàn)分離的分離,有利于分工合作、減少非技術(shù)人員對頁面的修改造成的WEB應(yīng)用程序錯誤、提高效率、也更加適用于現(xiàn)在的發(fā)布系統(tǒng)。也可以把以前的一些服務(wù)器負擔(dān)的工作轉(zhuǎn)嫁到客戶端,利于客戶端閑置的處理能力來處理。
四、應(yīng)用
Ajax理念的出現(xiàn),揭開了無刷新更新頁面時代的序幕,并有代替?zhèn)鹘y(tǒng)web開發(fā)中采用form(表單)遞交方式更新web頁面的趨勢,可以算是一個里程碑。但Ajax都不是適用于所有地方的,它的適用范圍是由它的特性所決定的。
舉個應(yīng)用的例子,是關(guān)于級聯(lián)菜單方面的Ajax應(yīng)用。
我們以前的對級聯(lián)菜單的處理是這樣的:
為了避免每次對菜單的操作引起的重載頁面,不采用每次調(diào)用后臺的方式,而是一次性將級聯(lián)菜單的所有數(shù)據(jù)全部讀取出來并寫入數(shù)組,然后根據(jù)用戶的操作用JavaScript來控制它的子集項目的呈現(xiàn),這樣雖然解決了操作響應(yīng)速度、不重載頁面以及避免向服務(wù)器頻繁發(fā)送請求的問題,但是如果用戶不對菜單進行操作或只對菜單中的一部分進行操作的話,那讀取的數(shù)據(jù)中的一部分就會成為冗余數(shù)據(jù)而浪費用戶的資源,特別是在菜單結(jié)構(gòu)復(fù)雜、數(shù)據(jù)量大的情況下(比如菜單有很多級、每一級菜又有上百個項目),這種弊端就更為突出。
如果在此案中應(yīng)用Ajax后,結(jié)果就會有所改觀:
在初始化頁面時我們只讀出它的第一級的所有數(shù)據(jù)并顯示,在用戶操作一級菜單其中一項時,會通過Ajax向后臺請求當(dāng)前一級項目所屬的二級子菜單的所有數(shù)據(jù),如果再繼續(xù)請求已經(jīng)呈現(xiàn)的二級菜單中的一項時,再向后面請求所操作二級菜單項對應(yīng)的所有三級菜單的所有數(shù)據(jù),以此類推……這樣,用什么就取什么、用多少就取多少,就不會有數(shù)據(jù)的冗余和浪費,減少了數(shù)據(jù)下載總量,而且更新頁面時不用重載全部內(nèi)容,只更新需要更新的那部分即可,相對于后臺處理并重載的方式縮短了用戶等待時間,也把對資源的浪費降到最低。
此外,Ajax由于可以調(diào)用外部數(shù)據(jù),也可以實現(xiàn)數(shù)據(jù)聚合的功能(當(dāng)然要有相應(yīng)授權(quán)),比如微軟剛剛在3月15日發(fā)布的在線RSS閱讀器BETA版;還可以利于一些開放的數(shù)據(jù),開發(fā)自已的一些應(yīng)用程序,比如用Amazon的數(shù)據(jù)作的一些新穎的圖書搜索應(yīng)用。
總之,Ajax適用于交互較多,頻繁讀數(shù)據(jù),數(shù)據(jù)分類良好的WEB應(yīng)用。
五、Ajax的優(yōu)勢
1、減輕服務(wù)器的負擔(dān)。因為Ajax的根本理念是“按需取數(shù)據(jù)”,所以最大可能在減少了冗余請求和響影對服務(wù)器造成的負擔(dān);
2、無刷新更新頁面,減少用戶實際和心理等待時間;
首先,“按需取數(shù)據(jù)”的模式減少了數(shù)據(jù)的實際讀取量,打個很形象的比方,如果說重載的方式是從一個終點回到原點再到另一個終點的話,那么Ajax就是以一個終點為基點到達另一個終點;
圖5-1
圖5-2
其次,即使要讀取比較大的數(shù)據(jù),也不用像RELOAD一樣出現(xiàn)白屏的情況,由于Ajax是用XMLHTTP發(fā)送請求得到服務(wù)端應(yīng)答數(shù)據(jù),在不重新載入整個頁面的情況下用Javascript操作DOM最終更新頁面的,所以在讀取數(shù)據(jù)的過程中,用戶所面對的也不是白屏,而是原來的頁面狀態(tài)(或者可以加一個LOADING的提示框讓用戶了解數(shù)據(jù)讀取的狀態(tài)),只有當(dāng)接收到全部數(shù)據(jù)后才更新相應(yīng)部分的內(nèi)容,而這種更新也是瞬間的,用戶幾乎感覺不到。總之用戶是很敏感的,他們能感覺到你對他們的體貼,雖然不太可能立竿見影的效果,但會在用戶的心中一點一滴的積累他們對網(wǎng)站的依賴。
3、更好的用戶體驗;
4、也可以把以前的一些服務(wù)器負擔(dān)的工作轉(zhuǎn)嫁到客戶端,利于客戶端閑置的處理能力來處理,減輕服務(wù)器和帶寬的負擔(dān),節(jié)約空間和帶寬租用成本;
5、Ajax由于可以調(diào)用外部數(shù)據(jù);
6、基于標(biāo)準(zhǔn)化的并被廣泛支持和技術(shù),并且不需要插件或下載小程序;
7、Ajax使WEB中的界面與應(yīng)用分離(也可以說是數(shù)據(jù)與呈現(xiàn)分離);
8、對于用戶和ISP來說是雙盈的。
六、Ajax的問題
1、一些手持設(shè)備(如手機、PDA等)現(xiàn)在還不能很好的支持Ajax;
2、用JavaScript作的Ajax引擎,JavaScript的兼容性和DeBug都是讓人頭痛的事;
3、Ajax的無刷新重載,由于頁面的變化沒有刷新重載那么明顯,所以容易給用戶帶來困擾――用戶不太清楚現(xiàn)在的數(shù)據(jù)是新的還是已經(jīng)更新過的;現(xiàn)有的解決有:在相關(guān)位置提示、數(shù)據(jù)更新的區(qū)域設(shè)計得比較明顯、數(shù)據(jù)更新后給用戶提示等;
4、對流媒體的支持沒有FLASH、Java Applet好;
七、結(jié)束語
更好的Ajax應(yīng)用,需要更多的客戶端的開發(fā),和對當(dāng)前的WEB應(yīng)用理念的思考,而且良好的用戶體驗,來源于為處處用戶考慮的理念,而不單純是某種技術(shù)。
——————————————————————————————————————————
AJAX開發(fā)簡略
現(xiàn)在,有一種越來越流行越熱的“老”技術(shù),可以徹底改變這種窘迫的局面。那就是AJAX。如今,隨著Gmail、Google-maps的應(yīng)用和各種瀏覽器的支持,AJAX正逐漸吸引全世界的眼球。
一、AJAX定義
AJAX(Asynchronous JavaScript and XML)其實是多種技術(shù)的綜合,包括Javascript、XHTML和CSS、DOM、XML和XSTL、XMLHttpRequest。其中:
使用XHTML和CSS標(biāo)準(zhǔn)化呈現(xiàn),使用DOM實現(xiàn)動態(tài)顯示和交互,使用XML和XSTL進行數(shù)據(jù)交換與處理,使用XMLHttpRequest對象進行異步數(shù)據(jù)讀取,使用Javascript綁定和處理所有數(shù)據(jù)。
在AJAX提出之前,業(yè)界對于上述技術(shù)都只是單獨的使用,沒有綜合使用,也是由于之前的技術(shù)需求所決定的。隨著應(yīng)用的廣泛,AJAX也成為香餑餑了。
二、現(xiàn)狀與需要解決的問題
傳統(tǒng)的Web應(yīng)用采用同步交互過程,這種情況下,用戶首先向HTTP服務(wù)器觸發(fā)一個行為或請求的呼求。反過來,服務(wù)器執(zhí)行某些任務(wù),再向發(fā)出請求的用戶返回一個HTML頁面。這是一種不連貫的用戶體驗,服務(wù)器在處理請求的時候,用戶多數(shù)時間處于等待的狀態(tài),屏幕內(nèi)容也是一片空白。如下圖:
自從采用超文本作為Web傳輸和呈現(xiàn)之后,我們都是采用這么一套傳輸方式。當(dāng)負載比較小的時候,這并不會體現(xiàn)出有什么不妥。可是當(dāng)負載比較大,響應(yīng)時間要很長,1分鐘、2分鐘……數(shù)分鐘的時候,這種等待就不可忍受了。嚴重的,超過響應(yīng)時間,服務(wù)器干脆告訴你頁面不可用。另外,某些時候,我只是想改變頁面一小部分的數(shù)據(jù),那為什么我必須重新加載整個頁面呢?!當(dāng)軟件設(shè)計越來越講究人性化的時候,這么糟糕的用戶體驗簡直與這種原則背道而馳。為什么老是要讓用戶等待服務(wù)器取數(shù)據(jù)呢?至少,我們應(yīng)該減少用戶等待的時間。現(xiàn)在,除了程序設(shè)計、編碼優(yōu)化和服務(wù)器調(diào)優(yōu)之外,還可以采用AJAX。
三、為什么使用AJAX
與傳統(tǒng)的Web應(yīng)用不同,AJAX采用異步交互過程。AJAX在用戶與服務(wù)器之間引入一個中間媒介,從而消除了網(wǎng)絡(luò)交互過程中的處理—等待—處理—等待缺點。用戶的瀏覽器在執(zhí)行任務(wù)時即裝載了AJAX引擎。AJAX引擎用JavaScript語言編寫,通常藏在一個隱藏的框架中。它負責(zé)編譯用戶界面及與服務(wù)器之間的交互。AJAX引擎允許用戶與應(yīng)用軟件之間的交互過程異步進行,獨立于用戶與網(wǎng)絡(luò)服務(wù)器間的交流。現(xiàn)在,可以用Javascript調(diào)用AJAX引擎來代替產(chǎn)生一個HTTP的用戶動作,內(nèi)存中的數(shù)據(jù)編輯、頁面導(dǎo)航、數(shù)據(jù)校驗這些不需要重新載入整個頁面的需求可以交給AJAX來執(zhí)行。
使用AJAX,可以為ISP、開發(fā)人員、終端用戶帶來可見的便捷:
l 減輕服務(wù)器的負擔(dān)。AJAX的原則是“按需取數(shù)據(jù)”,可以最大程度的減少冗余請求,和響應(yīng)對服務(wù)器造成的負擔(dān)。
l 無刷新更新頁面,減少用戶心理和實際的等待時間。特別的,當(dāng)要讀取大量的數(shù)據(jù)的時候,不用像Reload那樣出現(xiàn)白屏的情況,AJAX使用XMLHTTP對象發(fā)送請求并得到服務(wù)器響應(yīng),在不重新載入整個頁面的情況下用Javascript操作DOM最終更新頁面。所以在讀取數(shù)據(jù)的過程中,用戶所面對的不是白屏,是原來的頁面內(nèi)容(也可以加一個Loading的提示框讓用戶知道處于讀取數(shù)據(jù)過程),只有當(dāng)數(shù)據(jù)接收完畢之后才更新相應(yīng)部分的內(nèi)容。這種更新是瞬間的,用戶幾乎感覺不到。
l 帶來更好的用戶體驗。
l 可以把以前一些服務(wù)器負擔(dān)的工作轉(zhuǎn)嫁到客戶端,利用客戶端閑置的能力來處理,減輕服務(wù)器和帶寬的負擔(dān),節(jié)約空間和寬帶租用成本。
l 可以調(diào)用外部數(shù)據(jù)。
l 基于標(biāo)準(zhǔn)化的并被廣泛支持的技術(shù),不需要下載插件或者小程序。
l 進一步促進頁面呈現(xiàn)和數(shù)據(jù)的分離。
四、誰在使用AJAX
在應(yīng)用AJAX開發(fā)上面,Google當(dāng)仁不讓是表率。Orkut、Gmail、Google Groups、Google Maps、Google Suggest都應(yīng)用了這項技術(shù)。Amazon的A9.com搜索引擎也采用了類似的技術(shù)。
微軟也在積極開發(fā)更為完善的AJAX應(yīng)用:它即將推出代號為Atlas的AJAX工具。Atlas的功能超越了AJAX本身,包括整合Visual Studio的調(diào)試功能。另外,新的ASP.NET控件將使客戶端控件與服務(wù)器端代碼的捆綁更為簡便。Atlas客戶腳本框架(Atlas Clent Script Framework)也使與網(wǎng)頁及相關(guān)項目的交互更為便利。但Visual Studio 2005中并不包含此項功能。
微軟最近宣布Atlas客戶腳本框架將包含如下內(nèi)容(詳細資料請訪問Atlas計劃網(wǎng)站):
* 一個可擴展的核心框架,它添加了JavaScript功能:如生命同時期管理、繼承管理、多點傳送處理器和界面管理。
* 一個常見功能的基本類庫,有豐富的字符串處理、計時器和運行任務(wù)。
* 為HTML附加動態(tài)行為的用戶界面框架。
* 一組用來簡化服務(wù)器連通和網(wǎng)絡(luò)訪問的網(wǎng)絡(luò)堆棧。
* 一組豐富的用戶界面開發(fā)控件,如:自動完成的文本框、動畫和拖放。
* 處理瀏覽器腳本行為差異的瀏覽器兼容層面。
典型的,微軟將AJAX技術(shù)應(yīng)用在MSN Space上面。很多人一直都對MS Space服務(wù)感到很奇怪,當(dāng)提交回復(fù)評論以后,瀏覽器會暫時停頓一下,然后在無刷新的情況下把我提交的評論顯示出來。這個就是應(yīng)用了AJAX的效果。試想,如果添加一個評論就要重新刷新整個頁面,那可真費事。
目前,AJAX應(yīng)用最普遍的領(lǐng)域是GIS-Map方面。GIS的區(qū)域搜索強調(diào)快速響應(yīng),AJAX的特點正好符合這種需求。
五、用AJAX改進你的設(shè)計
AJAX雖然可以實現(xiàn)無刷新更新頁面內(nèi)容,但是也不是什么地方都可以用,主要應(yīng)用在交互較多、頻繁讀數(shù)據(jù)、數(shù)據(jù)分類良好的Web應(yīng)用中。現(xiàn)在,讓我們舉兩個例子,看看如何用AJAX改進你的設(shè)計。
例子1:數(shù)據(jù)校驗在輸入form表單內(nèi)容的時候,我們通常需要確保數(shù)據(jù)的唯一性。因此,常常在頁面上提供“唯一性校驗”按鈕,讓用戶點擊,打開一個校驗小窗口;或者等form提交到服務(wù)器端,由服務(wù)器判斷后在返回相應(yīng)的校驗信息。前者,window.open操作本來就是比較耗費資源的,通常由window. showModalDialog代替,即使這樣也要彈出一個對話框;后者,需要把整個頁面提交到服務(wù)器并由服務(wù)器判斷校驗,這個過程不僅時間長而且加重了服務(wù)器負擔(dān)。而使用AJAX,這個校驗請求可以由XMLHttpRequest對象發(fā)出,整個過程不需要彈出新窗口,也不需要將整個頁面提交到服務(wù)器,快速又不加重服務(wù)器負擔(dān)。
例子2:按需取數(shù)據(jù)—級聯(lián)菜單以前,為了避免每次對菜單的操作引起的重載頁面,不采用每次調(diào)用后臺的方式,而是一次性將級聯(lián)菜單的所有數(shù)據(jù)全部讀取出來并寫入數(shù)組,然后根據(jù)用戶的操作用JavaScript來控制它的子集項目的呈現(xiàn),這樣雖然解決了操作響應(yīng)速度、不重載頁面以及避免向服務(wù)器頻繁發(fā)送請求的問題,但是如果用戶不對菜單進行操作或只對菜單中的一部分進行操作的話,那讀取的數(shù)據(jù)中的一部分就會成為冗余數(shù)據(jù)而浪費用戶的資源,特別是在菜單結(jié)構(gòu)復(fù)雜、數(shù)據(jù)量大的情況下(比如菜單有很多級、每一級菜又有上百個項目),這種弊端就更為突出。
現(xiàn)在應(yīng)用AJAX,在初始化頁面時我們只讀出它的第一級的所有數(shù)據(jù)并顯示,在用戶操作一級菜單其中一項時,會通過Ajax向后臺請求當(dāng)前一級項目所屬的二級子菜單的所有數(shù)據(jù),如果再繼續(xù)請求已經(jīng)呈現(xiàn)的二級菜單中的一項時,再向后面請求所操作二級菜單項對應(yīng)的所有三級菜單的所有數(shù)據(jù),以此類推……這樣,用什么就取什么、用多少就取多少,就不會有數(shù)據(jù)的冗余和浪費,減少了數(shù)據(jù)下載總量,而且更新頁面時不用重載全部內(nèi)容,只更新需要更新的那部分即可,相對于后臺處理并重載的方式縮短了用戶等待時間,也把對資源的浪費降到最低。
例子3:讀取外部數(shù)據(jù)AJAX可以調(diào)用外部數(shù)據(jù),因此,可以對一些開發(fā)的數(shù)據(jù)比如XML文檔、RSS文檔進行二次加工,實現(xiàn)數(shù)據(jù)整合或者開發(fā)應(yīng)用程序。
六、AJAX的缺陷
AJAX不是完美的技術(shù)。使用AJAX,它的一些缺陷不得不權(quán)衡一下:
l AJAX大量使用了Javascript和AJAX引擎,而這個取決于瀏覽器的支持。IE5.0及以上、Mozilla1.0、NetScape7及以上版本才支持,Mozilla雖然也支持AJAX,但是提供XMLHttpRequest的方式不一樣。所以,使用AJAX的程序必須測試針對各個瀏覽器的兼容性。
l AJAX更新頁面內(nèi)容的時候并沒有刷新整個頁面,因此,網(wǎng)頁的后退功能是失效的;有的用戶還經(jīng)常搞不清楚現(xiàn)在的數(shù)據(jù)是舊的還是已經(jīng)更新過的。這個就需要在明顯位置提醒用戶“數(shù)據(jù)已更新”。
l 對流媒體的支持沒有FLASH、Java Applet好。
l 一些手持設(shè)備(如手機、PDA等)現(xiàn)在還不能很好的支持Ajax。
七、AJAX開發(fā)
到這里,已經(jīng)可以清楚的知道AJAX是什么,AJAX能做什么,AJAX什么地方不好。如果你覺得AJAX真的能給你的開發(fā)工作帶來改進的話,那么繼續(xù)看看怎么使用AJAX吧。
7.1、AJAX應(yīng)用到的技術(shù)AJAX涉及到的7項技術(shù)中,個人認為Javascript、XMLHttpRequest、DOM、XML比較有用。
A、XMLHttpRequest對象XMLHttpRequest是XMLHTTP組件的對象,通過這個對象,AJAX可以像桌面應(yīng)用程序一樣只同服務(wù)器進行數(shù)據(jù)層面的交換,而不用每次都刷新界面,也不用每次將數(shù)據(jù)處理的工作都交給服務(wù)器來做;這樣既減輕了服務(wù)器負擔(dān)又加快了響應(yīng)速度、縮短了用戶等待的時間。
IE5.0開始,開發(fā)人員可以在Web頁面內(nèi)部使用XMLHTTP ActiveX組件擴展自身的功能,不用從當(dāng)前的Web頁面導(dǎo)航就可以直接傳輸數(shù)據(jù)到服務(wù)器或者從服務(wù)器接收數(shù)據(jù)。,Mozilla1.0以及NetScape7則是創(chuàng)建繼承XML的代理類XMLHttpRequest;對于大多數(shù)情況,XMLHttpRequest對象和XMLHTTP組件很相似,方法和屬性類似,只是部分屬性不同。
XMLHttpRequest對象初始化:
XMLHttpRequest對象的方法:
方法 | 描述 |
abort() | 停止當(dāng)前請求 |
getAllResponseHeaders() | 作為字符串返回完整的headers |
getResponseHeader("headerLabel") | 作為字符串返回單個的header標(biāo)簽 |
open("method","URL"[,asyncFlag[,"userName"[, "password"]]]) | 設(shè)置未決的請求的目標(biāo) URL,方法,和其他參數(shù) |
send(content) | 發(fā)送請求 |
setRequestHeader("label", "value") | 設(shè)置header并和請求一起發(fā)送 |
XMLHttpRequest對象的屬性:
屬性 | 描述 |
onreadystatechange | 狀態(tài)改變的事件觸發(fā)器 |
readyState | 對象狀態(tài)(integer): 0 = 未初始化 1 = 讀取中 2 = 已讀取 3 = 交互中 4 = 完成 |
responseText | 服務(wù)器進程返回數(shù)據(jù)的文本版本 |
responseXML | 服務(wù)器進程返回數(shù)據(jù)的兼容DOM的XML文檔對象 |
status | 服務(wù)器返回的狀態(tài)碼, 如:404 = "文件未找到" 、200 ="成功" |
statusText | 服務(wù)器返回的狀態(tài)文本信息 |
B、JavascriptJavascript一直被定位為客戶端的腳本語言,應(yīng)用最多的地方是表單數(shù)據(jù)的校驗。現(xiàn)在,可以通過Javascript操作XMLHttpRequest,來跟數(shù)據(jù)庫打交道。
C、DOMDOM(Document Object Model)是提供給HTML和XML使用的一組API,提供了文件的表述結(jié)構(gòu),并可以利用它改變其中的內(nèi)容和可見物。腳本語言通過DOM才可以跟頁面進行交互。Web開發(fā)人員可操作及建立文件的屬性、方法以及事件都以對象來展現(xiàn)。比如,document就代表頁面對象本身。
D、XML通過XML(Extensible Markup Language),可以規(guī)范的定義結(jié)構(gòu)化數(shù)據(jù),是網(wǎng)上傳輸?shù)臄?shù)據(jù)和文檔符合統(tǒng)一的標(biāo)準(zhǔn)。用XML表述的數(shù)據(jù)和文檔,可以很容易的讓所有程序共享。
7.2、AJAX開發(fā)框架這里,我們通過一步步的解析,來形成一個發(fā)送和接收XMLHttpRequest請求的程序框架。AJAX實質(zhì)上也是遵循Request/Server模式,所以這個框架基本的流程也是:對象初始化à發(fā)送請求à服務(wù)器接收à服務(wù)器返回à客戶端接收à修改客戶端頁面內(nèi)容。只不過這個過程是異步的。
A、初始化對象并發(fā)出XMLHttpRequest請求為了讓Javascript可以向服務(wù)器發(fā)送HTTP請求,必須使用XMLHttpRequest對象。使用之前,要先將XMLHttpRequest對象實例化。之前說過,各個瀏覽器對這個實例化過程實現(xiàn)不同。IE以ActiveX控件的形式提供,而Mozilla等瀏覽器則直接以XMLHttpRequest類的形式提供。為了讓編寫的程序能夠跨瀏覽器運行,要這樣寫:
if (window.XMLHttpRequest) { // Mozilla, Safari, ...
http_request = new XMLHttpRequest();
} else if (window.ActiveXObject) { // IE
http_request = new ActiveXObject("Microsoft.XMLHTTP");
}
有些版本的Mozilla瀏覽器處理服務(wù)器返回的未包含XML mime-type頭部信息的內(nèi)容時會出錯。因此,要確保返回的內(nèi)容包含text/xml信息。
http_request = new XMLHttpRequest();
http_request.overrideMimeType('text/xml');
B、指定響應(yīng)處理函數(shù)接下來要指定當(dāng)服務(wù)器返回信息時客戶端的處理方式。只要將相應(yīng)的處理函數(shù)名稱賦給XMLHttpRequest對象的onreadystatechange屬性就可以了。比如:
http_request.onreadystatechange = processRequest;
需要指出的時,這個函數(shù)名稱不加括號,不指定參數(shù)。也可以用Javascript即時定義函數(shù)的方式定義響應(yīng)函數(shù)。比如:
http_request.onreadystatechange = function() {
};
C、發(fā)出HTTP請求指定響應(yīng)處理函數(shù)之后,就可以向服務(wù)器發(fā)出HTTP請求了。這一步調(diào)用XMLHttpRequest對象的open和send方法。
http_request.open('GET', 'http://www.example.org/some.file', true);
http_request.send(null);
open的第一個參數(shù)是HTTP請求的方法,為Get、Post或者Head。
第二個參數(shù)是目標(biāo)URL。基于安全考慮,這個URL只能是同網(wǎng)域的,否則會提示“沒有權(quán)限”的錯誤。這個URL可以是任何的URL,包括需要服務(wù)器解釋執(zhí)行的頁面,不僅僅是靜態(tài)頁面。
第三個參數(shù)只是指定在等待服務(wù)器返回信息的時間內(nèi)是否繼續(xù)執(zhí)行下面的代碼。如果為True,則不會繼續(xù)執(zhí)行,直到服務(wù)器返回信息。默認為True。
按照順序,open調(diào)用完畢之后要調(diào)用send方法。send的參數(shù)如果是以Post方式發(fā)出的話,可以是任何想傳給服務(wù)器的內(nèi)容。不過,跟form一樣,如果要傳文件給服務(wù)器,必須先調(diào)用setRequestHeader方法,修改MIME類別。如下:
http_request.setRequestHeader(“Content-Type”,”application/x-www-form-urlencoded”);
D、處理服務(wù)器返回的信息在第二步我們已經(jīng)指定了響應(yīng)處理函數(shù),這一步,來看看這個響應(yīng)處理函數(shù)都應(yīng)該做什么。
首先,它要檢查XMLHttpRequest對象的readyState值,判斷請求目前的狀態(tài)。參照前文的屬性表可以知道,readyState值為4的時候,代表服務(wù)器已經(jīng)傳回所有的信息,可以開始處理信息并更新頁面內(nèi)容了。如下:
if (http_request.readyState == 4) {
// 信息已經(jīng)返回,可以開始處理
} else {
// 信息還沒有返回,等待
}
服務(wù)器返回信息后,還需要判斷返回的HTTP狀態(tài)碼,確定返回的頁面沒有錯誤。所有的狀態(tài)碼都可以在W3C的官方網(wǎng)站上查到。其中,200代表頁面正常。
if (http_request.status == 200) {
// 頁面正常,可以開始處理信息
} else {
// 頁面有問題
}
XMLHttpRequest對成功返回的信息有兩種處理方式:
responseText:將傳回的信息當(dāng)字符串使用;
responseXML:將傳回的信息當(dāng)XML文檔使用,可以用DOM處理。
E、一個初步的開發(fā)框架總結(jié)上面的步驟,我們整理出一個初步的可用的開發(fā)框架,供以后調(diào)用;這里,將服務(wù)器返回的信息用window.alert以字符串的形式顯示出來:
var http_request = false;
function send_request(url) {//初始化、指定處理函數(shù)、發(fā)送請求的函數(shù)
http_request = false;
//開始初始化XMLHttpRequest對象
if(window.XMLHttpRequest) { //Mozilla 瀏覽器
http_request = new XMLHttpRequest();
if (http_request.overrideMimeType) {//設(shè)置MiME類別
http_request.overrideMimeType("text/xml");
}
}
else if (window.ActiveXObject) { // IE瀏覽器
try {
http_request = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
http_request = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e) {}
}
}
if (!http_request) { // 異常,創(chuàng)建對象實例失敗
window.alert("不能創(chuàng)建XMLHttpRequest對象實例.");
return false;
}
http_request.onreadystatechange = processRequest;
// 確定發(fā)送請求的方式和URL以及是否同步執(zhí)行下段代碼
http_request.open("GET", url, true);
http_request.send(null);
}
// 處理返回信息的函數(shù)
function processRequest() {
if (http_request.readyState == 4) { // 判斷對象狀態(tài)
if (http_request.status == 200) { // 信息已經(jīng)成功返回,開始處理信息
alert(http_request.responseText);
} else { //頁面不正常
alert("您所請求的頁面有異常。");
}
}
}
7.3、簡單的示例接下來,我們利用上面的開發(fā)框架來做兩個簡單的應(yīng)用。
A、數(shù)據(jù)校驗在用戶注冊的表單中,經(jīng)常碰到要檢驗待注冊的用戶名是否唯一。傳統(tǒng)的做法是采用window.open的彈出窗口,或者window. showModalDialog的對話框。不過,這兩個都需要打開窗口。采用AJAX后,采用異步方式直接將參數(shù)提交到服務(wù)器,用window.alert將服務(wù)器返回的校驗信息顯示出來。代碼如下:
在之間增加一段form表單代碼:
在開發(fā)框架的基礎(chǔ)上再增加一個調(diào)用函數(shù):
function userCheck() {
var f = document.form1;
var username = f.username.value;
if(username=="") {
window.alert("用戶名不能為空。");
f.username.focus();
return false;
}
else {
send_request('sample1_2.jsp?username='+username);
}
}
看看sample1_2.jsp做了什么:
]]>
String username = request.getParameter("username");
if("educhina".equals(username)) out.print("用戶名已經(jīng)被注冊,請更換一個用戶名。");
else out.print("用戶名尚未被使用,您可以繼續(xù)。");
%>]]>
運行一下,嗯,沒有彈出窗口,沒有頁面刷新,跟預(yù)想的效果一樣。如果需要的話,可以在sample1_2.jsp中實現(xiàn)更復(fù)雜的功能。最后,只要將反饋信息打印出來就可以了。
B、級聯(lián)菜單我們在第五部分提到利用AJAX改進級聯(lián)菜單的設(shè)計。接下來,我們就演示一下如何“按需取數(shù)據(jù)”。
首先,在中間增加如下HTML代碼:
? |
? |
在框架的基礎(chǔ)上增加一個響應(yīng)函數(shù)showRoles(obj):
//顯示部門下的崗位
function showRoles(obj) {
document.getElementById(obj).parentNode.style.display = "";
document.getElementById(obj).innerHTML = "正在讀取數(shù)據(jù)..."
currentPos = obj;
send_request("sample2_2.jsp?playPos="+obj);
}
修改框架的processRequest函數(shù):
// 處理返回信息的函數(shù)
function processRequest() {
if (http_request.readyState == 4) { // 判斷對象狀態(tài)
if (http_request.status == 200) { // 信息已經(jīng)成功返回,開始處理信息
document.getElementById(currentPos).innerHTML = http_request.responseText;
} else { //頁面不正常
alert("您所請求的頁面有異常。");
}
}
}
最后就是smaple2_2.jsp了:
]]>
String playPos = request.getParameter("playPos");
if("pos_1".equals(playPos))
out.print(" 總經(jīng)理
副總經(jīng)理");
else if("pos_2".equals(playPos))
out.println(" 總工程師
軟件工程師");
%>]]>
運行一下看看效果:
————————————————————————————————————————
AJAX編寫用戶注冊實例及技術(shù)小結(jié)
下面我們來看代碼吧。
HTML頁面的完整代碼如下:
1<%@page language="java" contentType="text/html;charset=GBK"%> 2<script language="javascript" type="text/javascript"> 3<!-- 4/**//**Ajax 開始 by Alpha 2005-12-31*/ 5 6 var http = getHTTPObject(); 7 8 function handleHttpResponse(){ 9 if(http.readyState == 4){ 10 if(http.status == 200){ 11 var xmlDocument = http.responseXML; 12 if(http.responseText!=""){ 13 document.getElementById("showStr").style.display = ""; 14 document.getElementById("userName").style.background= "#FF0000"; 15 document.getElementById("showStr").innerText = http.responseText; 16 }else{ 17 document.getElementById("userName").style.background= "#FFFFFF"; 18 document.getElementById("showStr").style.display = "none"; 19 } 20 21 } 22 else{ 23 alert("你所請求的頁面發(fā)生異常,可能會影響你瀏覽該頁的信息!"); 24 alert(http.status); 25 } 26 } 27 } 28 29 function handleHttpResponse1(){ 30 if(http.readyState == 4){ 31 if(http.status == 200){ 32 var xmlDocument = http.responseXML; 33 if(http.responseText!=""){ 34 document.getElementById("comNmStr").style.display = ""; 35 document.getElementById("comNm").style.background= "#FF0000"; 36 document.getElementById("comNmStr").innerText = http.responseText; 37 }else{ 38 document.getElementById("comNm").style.background= "#FFFFFF"; 39 document.getElementById("comNmStr").style.display = "none"; 40 } 41 42 } 43 else{ 44 alert("你所請求的頁面發(fā)生異常,可能會影響你瀏覽該頁的信息!"); 45 alert(http.status); 46 } 47 } 48 } 49 50 function chkUser(){ 51 var url = "/chkUserAndCom"; 52 var name = document.getElementById("userName").value; 53 url += ("&userName="+name+"&oprate=chkUser"); 54 http.open("GET",url,true); 55 http.onreadystatechange = handleHttpResponse; 56 http.send(null); 57 return ; 58 } 59 function chkComNm(){ 60 var url = "/chkUserAndCom"; 61 var name = document.getElementById("comNm").value; 62 url += ("&comName="+name+"&oprate=chkCom"); 63 http.open("GET",url,true); 64 http.onreadystatechange = handleHttpResponse1; 65 http.send(null); 66 return ; 67 } 68 69 //該函數(shù)可以創(chuàng)建我們需要的XMLHttpRequest對象 70 function getHTTPObject(){ 71 var xmlhttp = false; 72 if(window.XMLHttpRequest){ 73 xmlhttp = new XMLHttpRequest(); 74 if(xmlhttp.overrideMimeType){ 75 xmlhttp.overrideMimeType('text/xml'); 76 } 77 } 78 else{ 79 try{ 80 xmlhttp = new ActiveXObject("Msxml2.XMLHTTP"); 81 }catch(e){ 82 try{ 83 xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); 84 }catch(E){ 85 xmlhttp = false; 86 } 87 } 88 } 89 return xmlhttp; 90 } 91/**//**Ajax 結(jié)束*/ 92 93//檢測表單 94function chkpassword() 95{ 96 var m=document.form1; 97 if(len(m.password.value) > 20 || len(m.password.value) < 5 || !isStr(m.password.value)) 98 { 99 document.getElementById("passwordStr").style.display = ""; 100 document.getElementById("password").style.background= "#FF0000"; 101 document.getElementById("passwordStr").innerText = "對不起,密碼必須為英文字母、數(shù)字或下劃線,長度為5~20!"; 102 } 103 else 104 { 105 document.getElementById("password").style.background= "#FFFFFF"; 106 document.getElementById("passwordStr").style.display = "none"; 107 } 108} 109 110function chkconfirmPassword() 111{ 112 var m=document.form1; 113 if (m.password.value != m.confirmPassword.value) 114 { 115 document.getElementById("confirmPasswordStr").style.display = ""; 116 document.getElementById("confirmPassword").style.background= "#FF0000"; 117 document.getElementById("confirmPasswordStr").innerText = "對不起,密碼與重復(fù)密碼不一致!"; 118 } 119 else 120 { 121 document.getElementById("confirmPassword").style.background= "#FFFFFF"; 122 document.getElementById("confirmPasswordStr").style.display = "none"; 123 } 124} 125 126function checkfield() 127{ 128 var m=document.form1; 129 if(m.userName.value.length==0) 130 { 131 alert("對不起,用戶名必須為英文字母、數(shù)字或下劃線,長度為5~20。"); 132 m.userName.focus(); 133 return false; 134 } 135 if(m.password.value.length==0) 136 { 137 alert("對不起,密碼必須為英文字母、數(shù)字或下劃線,長度為5~20。"); 138 m.password.focus(); 139 return false; 140 } 141 if (m.password.value != m.confirmPassword.value) 142 { 143 alert("對不起,密碼與重復(fù)密碼不一致!"); 144 m.confirmPassword.focus(); 145 return false; 146 } 147 if(m.comNm.value.length==0) 148 { 149 alert("對不起,企業(yè)名稱不能為空!!"); 150 m.comNm.focus(); 151 return false; 152 } 153 m.submit(); 154} 155 156//--> 157</script> 158<body topmargin="0"> 159<form name="form1" method="post" action="/Control?act=Register"> 160<table width="100%"> 161 <tr><td align="center">?<H2>Ajax 演示程序</H1></td></tr> 162 <tr><td align="center">????------ 企業(yè)注冊 By Alpha</td></tr> 163</table> 164 165<HR> 166 <table width="400" border="0" cellpadding="1" cellspacing="1" align="center" > 167 <tr> 168 <td><font color="red">*</font></td> 169 <td>用戶帳號:</td> 170 <td> 171 <input type="text" name="userName" maxlength="20" style="background:#FFFFFF" onBlur="chkUser()" value=""/> 172 <div id="showStr" style="background-color:#FF9900;display:none"></div> 173 </td> 174 </tr> 175 <tr> 176 <td><font color="red">*</font></td> 177 <td>企業(yè)名稱:</td> 178 <td> 179 <input type="text" name="comNm" maxlength="100" style="background:#FFFFFF" onBlur="chkComNm()" value=""/> 180 <div id="comNmStr" style="background-color:#FF9900;display:none"></div> 181 182 </td> 183 </tr> 184 <tr> 185 <td><font color="red">*</font></td> 186 <td>用戶密碼:</td> 187 <td><input type="password" name="password" maxlength="20" style="background:#FFFFFF" onBlur="chkpassword()"/> 188 <div id="passwordStr" style="background-color:#FF9900;display:none"></div> 189 </td> 190 </tr> 191 <tr> 192 <td><font color="red">*</font></td> 193 <td>確認密碼:</td> 194 <td><input type="password" name="confirmPassword" maxlength="20" style="background:#FFFFFF" onBlur="chkconfirmPassword()"/> 195 <div id="confirmPasswordStr" style="background-color:#FF9900;display:none"></div> 196 </td> 197 </tr> 198 </table> 199 200 <div align="center"> 201 <input type="button" name="ok" value=" 確 定 " onclick="checkfield()"> 202 ? 203 <input type="reset" name="reset" value=" 取 消 "> 204 </div> 205 206</form> 207</body> 208</html> |
用JavaScript來創(chuàng)建XMLHttpRequest 類向服務(wù)器發(fā)送一個HTTP請求后,接下來要決定當(dāng)收到服務(wù)器的響應(yīng)后,需要做什么。這需要告訴HTTP請求對象用哪一個JavaScript函數(shù)處理這個響應(yīng)。可以將對象的onreadystatechange屬性設(shè)置為要使用的JavaScript的函數(shù)名,如下所示:xmlhttp_request.onreadystatechange =FunctionName;
FunctionName是用JavaScript創(chuàng)建的函數(shù)名,注意不要寫成FunctionName(),當(dāng)然我們也可以直接將JavaScript代碼創(chuàng)建在onreadystatechange之后。
我們調(diào)用request.open()-它用服務(wù)器打開套接字頻道,使用一個HTTP動詞(GET或POST)作為第一個參數(shù)并且以數(shù)據(jù)提供者的URL作為第二個參數(shù)。request.open()的最后一個參數(shù)被設(shè)置為true-它指示該請求的異步特性。注意,該請求還沒有被提交。隨著對request.send()的調(diào)用,開始提交-這可以為POST提供任何必要的有效載荷。在使用異步請求時,我們必須使用request.onreadystatechanged屬性來分配請求的回調(diào)函數(shù)。(如果請求是同步的話,我們應(yīng)該能夠在調(diào)用request.send之后立即處理結(jié)果,但是我們也有可能阻斷用戶,直到該請求完成為止。)
我看再看看數(shù)據(jù)提供者的URL,url = "/chkUserAndCom",servlet如下:
1/**//* 2 * Created on 2005-12-31 3 * 4 * TODO To change the template for this generated file go to 5 * Window - Preferences - Java - Code Style - Code Templates 6 */ 7package com.event; 8 9import javax.servlet.ServletException; 10import javax.servlet.http.HttpServletRequest; 11import javax.servlet.http.HttpServletResponse; 12 13import com.beans.EBaseInfo; 14 15/** *//** 16 * @author Alpha 2005-12-31 17 * 18 * <P>Ajax 演示---企業(yè)注冊時檢查企業(yè)用戶名和企業(yè)名稱</P> 19 * 20 * TODO To change the template for this generated type comment go to 21 * Window - Preferences - Java - Code Style - Code Templates 22 */ 23public class CheckUserAndComNm { 24 private String msgStr = ""; 25 protected void doGet(HttpServletRequest request,HttpServletResponse response) 26 throws ServletException 27 { 28 29 EComBaseInfo info=new EComBaseInfo(); 30 String oprate=request.getParameter("oprate")).trim(); 31 String userName=request.getParameter("userName"); 32 String passWord=request.getParameter("password"); 33 String comName=request.getParameter("comName"); 34 35 try 36 { 37 if(oprate.equals("chkUser")) 38 { 39 response.setContentType("text/html;charset=GB2312"); 40 if(userName.length()<5||userName.length()>20) 41 { 42 msgStr = "對不起,用戶名必須為字母、數(shù)字或下劃線,長度為5-20個字符!"; 43 } 44 else 45 { 46 boolean bTmp=info.findUser(userName); //找查數(shù)據(jù)庫中有無該用戶名 47 if(bTmp) 48 msgStr ="對不起,此用戶名已經(jīng)存在,請更換用戶名注冊!"; 49 else 50 msgStr =""; 51 } 52 response.getWriter().write(msgStr); 53 } 54 else if(oprate.equals("chkCom")) 55 { 56 response.setContentType("text/html;charset=GB2312"); 57 if(comName.length()<6||comName.length()>100) 58 { 59 msgStr = "對不起,公司名稱長度為6-100個字符(不包括字符內(nèi)的空格)!"; 60 } 61 else 62 { 63 boolean bTmp=info.findCom(comName); //找查數(shù)據(jù)庫中有無該企業(yè)名 64 if(bTmp) 65 msgStr ="對不起,此企業(yè)名稱已經(jīng)存在,請更換企業(yè)名稱注冊!"; 66 else 67 msgStr =""; 68 } 69 response.getWriter().write(msgStr); 70 71 } 72 } 73 catch(Exception ex) 74 { 75 } 76 finally 77 { 78 request.setAttribute("url",url); 79 } 80 } 81 82 protected void doPost(HttpServletRequest request,HttpServletResponse response) 83 throws ServletException 84 { 85 doGet(request,response); 86 } 87} 88 |
AJAX技術(shù)小結(jié)
1. Ajax(Asynchronous JavaScript and XML)是一個結(jié)合了Java技術(shù)、XML、以及JavaScript的編程技術(shù),可以讓你構(gòu)建基于Java技術(shù)的Web應(yīng)用,并打破了使用頁面重載的慣例。
2. Ajax,異步JavaScript與XML,是使用客戶端腳本與Web服務(wù)器交換數(shù)據(jù)的Web應(yīng)用開發(fā)方法。這樣,Web頁面不用打斷交互流程進行重新加裁,就可以動態(tài)地更新。使用Ajax,你可以創(chuàng)建接近本地桌面應(yīng)用的,直接的、高可用的、更豐富的、更動態(tài)的Web用戶接口界面。
3. 對于Mozilla﹑Netscape﹑Safari、Firefox等瀏覽器,創(chuàng)建XMLHttpRequest 方法如下:
xmlhttp_request = new XMLHttpRequest();
4. IE等創(chuàng)建XMLHttpRequest 方法如下:
xmlhttp = new ActiveXObject("Msxml2.XMLHTTP")
或 xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
5. xmlhttp_request.open('GET', URL, true); xmlhttp_request.send(null);
6. open()的第一個參數(shù)是HTTP請求方式—GET,POST或任何服務(wù)器所支持的您想調(diào)用的方式。 按照HTTP規(guī)范,該參數(shù)要大寫;否則,某些瀏覽器(如Firefox)可能無法處理請求。第二個參數(shù)是請求頁面的URL。第三個參數(shù)設(shè)置請求是否為異步模式。如果是TRUE,JavaScript函數(shù)將繼續(xù)執(zhí)行,而不等待服務(wù)器響應(yīng)。這就是"AJAX"中的"A"。
Ajax技術(shù)運用的好的話,給我們的網(wǎng)頁增添了許多友好的效果,給用戶還來更好的感覺。Ajax是個好東西。
————————————————————————————————————————
利用AJAX技術(shù)開發(fā)應(yīng)用程序?qū)崙?zhàn)
一、 簡介
AJAX,一個異步JavaScript和XML的縮略詞,是最近出來的技術(shù)詞語。異步意味著你可以經(jīng)由超文本傳輸協(xié)議(HTTP)向一個服務(wù)器發(fā)出請求并且在等待該響應(yīng)時繼續(xù)處理另外的數(shù)據(jù)。這就意味著,例如,你可以調(diào)用一個服務(wù)器端腳本來從一個數(shù)據(jù)庫中以XML方式檢索數(shù)據(jù),把數(shù)據(jù)發(fā)送到存儲在一個數(shù)據(jù)庫的服務(wù)器腳本,或者簡單地裝載一個XML文件以填充你的Web站點而不需刷新該頁面。然而,在這項新技術(shù)提供巨大能力的同時,它也引起了在"Back"按鈕問題上的很多爭論。本文將幫助你確定在真實世界中何時使用AJAX是最佳選擇。
首先,我假定你對縮略詞JavaScript和XML部分有一個基本了解。盡管你能通過AJAX請求任何類型的文本文件,但是我在此主要集中討論XML。我將解釋怎樣在真實世界中使用AJAX以及怎樣在一個工程中評估它的價值。在你讀完本文后,你將會明白什么是AJAX,在什么情況下,為什么以及怎樣使用這項技術(shù)。你將要學(xué)習(xí),在保持給用戶提供直觀體驗的同時怎樣創(chuàng)建對象,發(fā)出請求以及定制響應(yīng)。
我已創(chuàng)建了一個適合于本文的示例工程(你可以下載源代碼)。這個示例實現(xiàn)了一個簡單的請求-它裝載一個包含頁面內(nèi)容的XML文件并且分析數(shù)據(jù)以把它顯示在一個HTML頁面中。
二、 常規(guī)屬性和方法
表1和2提供了一個屬性和方法的概述-它們?yōu)?Windows Internet Explorer 5,Mozilla,Netscape 7,Safari 1.2,和 Opera 等瀏覽器所支持。
表1屬性
屬性 | 描述 |
onreadystatechange | 當(dāng)請求對象變化時該事件處理器激活。 |
readyState | 返回指示對象的當(dāng)前狀態(tài)的值。 |
responseText | 來自服務(wù)器的響應(yīng)串的版本。 |
responseXML | 來自服務(wù)器的響應(yīng)的DOM兼容的文檔對象。 |
status | 來自服務(wù)器的響應(yīng)的狀態(tài)碼。 |
statusText | 以一個字符串形式返回的狀態(tài)消息。 |
表2方法
方法 | 描述 |
Abort() | 取消當(dāng)前HTTP請求。 |
getAllResponseHeaders() | 檢索所有的HTTP頭值。 |
getResponseHeader("headerLabel") | 從響應(yīng)體中檢索一個HTTP頭部的值。 |
open("method","URL"[,asyncFlag[,"userName"[,"password"]]]) | 初始化一個MSXML2.XMLHTTP請求,并從該請求指定方法,URL和認證信息。 |
send(content) | 發(fā)送一個HTTP請求到服務(wù)器并接收響應(yīng)。 |
setRequestHeader("label", "value") | 指定一個HTTP頭的名字。 |
三、 從哪里開始
首先,你需要創(chuàng)建XML文件-后面我們對之進行請求并作為頁面內(nèi)容進行分析。你正在請求的文件必須與目標(biāo)工程駐留在相同的服務(wù)器上。
下一步,創(chuàng)建發(fā)出請求的HTML文件。當(dāng)頁面通過使用頁面主體中的onload方法進行加載時,該請求發(fā)生。接著,該文件需要一個有ID的div標(biāo)簽,這樣當(dāng)我們準(zhǔn)備好要顯示內(nèi)容時就可以對之進行 定位 。當(dāng)你做完所有這些,你的頁面的主體看上去如下:
<body onload="makeRequest('xml/content.xml');"> <div id="copy"></div> </body> |
四、 創(chuàng)建請求對象
為了創(chuàng)建請求對象,你必須檢查是否瀏覽器使用XMLHttpRequest或ActiveXObject。這兩個對象之間的主要區(qū)別在于使用它們的瀏覽器。Windows IE 5 及以上版本使用ActiveX對象;而Mozilla,Netscape 7,Opera和Safari 1.2及以上版本使用XMLHttpRequest對象。另外一個區(qū)別是你創(chuàng)建對象的方式:Opera,Mozilla,Netscape和Safari允許你簡單地調(diào)用該對象的構(gòu)造器,但是Windows IE需要把對象的名字傳遞到ActiveX構(gòu)造器中。下面是怎樣創(chuàng)建代碼來決定要使用哪個對象和怎樣創(chuàng)建它的示例:
if(window.XMLHttpRequest) { request = new XMLHttpRequest();} else if(window.ActiveXObject) { request = new ActiveXObject("MSXML2.XMLHTTP");} |
五、 發(fā)出請求
現(xiàn)在既然你已經(jīng)創(chuàng)建了你的請求對象,那么你已經(jīng)為向服務(wù)器發(fā)出請求作了準(zhǔn)備。創(chuàng)建一個到事件處理器的參考以聽取onreadystatechange事件。然后,該事件處理器方法將在狀態(tài)發(fā)生變化時作出響應(yīng)。一旦我們完成請求,我們就開始創(chuàng)建這個方法。打開連接以GET或POST一個定制的URL-在此是一個content.xml,并且設(shè)置一個布爾定義-是否你想要進行異步調(diào)用。
現(xiàn)在到了發(fā)出請求的時間了。在這個示例中,我使用了null,因為我們使用的是GET;為了使用POST,你需要使用下面這個方法發(fā)出一個查詢串:
request.onreadystatechange = onResponse; request.open("GET". url, true); request.send(null); |
六、 定制加載和錯誤處理消息
你為onreadystatechange方法創(chuàng)建的事件處理器正是集中進行加載和處理錯誤的場所。現(xiàn)在到了考慮用戶并針對他們與之交互的內(nèi)容的狀態(tài)提供反饋的時候了。在這個實例中,我針對所有的裝載狀態(tài)代碼提供反饋,并且也對最經(jīng)常發(fā)生的錯誤處理狀態(tài)代碼提供一些基本的反饋。為了顯示請求對象的當(dāng)前狀態(tài),readyState屬性包括顯示在下表中的一些值。
值 | 描述 |
0 | 未初始化,對象沒有用數(shù)據(jù)進行初始化。 |
1 | 裝載中,對象正在裝載它的數(shù)據(jù)。 |
2 | 裝載結(jié)束,對象完成了它的數(shù)據(jù)的裝載。 |
3 | 可交互,用戶能與對象交互了,盡管它還沒有裝載結(jié)束。 |
4 | 完成,對象已經(jīng)完全被初始化。 |
W3C中有很長的一串有關(guān)HTTP狀態(tài)代碼的定義。我選擇了兩個狀態(tài)代碼:
·200:請求成功了。
·404:服務(wù)器沒有找到與所請求的文件相匹配的任何東西。
最后,我檢查任何另外的狀況代碼-它們將生成一個錯誤并提供一個一般錯誤信息。下面是一個代碼示例-你可以用之來處理這些情況。注意,我在定位我們前面在HTML文件的主體中創(chuàng)建的div ID并且對它應(yīng)用裝載和/或錯誤信息-通過innerHTML方法-這個方法用于設(shè)置在div對象的開始和結(jié)束標(biāo)簽之間的HTML:
if(obj.readyState == 0) { document.getElementById('copy').innerHTML = "Sending Request...";} if(obj.readyState == 1) { document.getElementById('copy').innerHTML = "Loading Response...";} if(obj.readyState == 2) { document.getElementById('copy').innerHTML = "Response Loaded...";} if(obj.readyState == 3) { document.getElementById('copy').innerHTML = "Response Ready...";} if(obj.readyState == 4){ if(obj.status == 200){ return true; } else if(obj.status == 404) { // 添加一個定制消息或把用戶重定向到另外一個頁面 document.getElementById('copy').innerHTML = "File not found"; } else {document.getElementById('copy').innerHTML = "There was a problem retrieving the XML."; } } |
當(dāng)狀況代碼為200時,這意味著請求成功。下面開始進行響應(yīng)了。
七、 分析響應(yīng)
當(dāng)你準(zhǔn)備好分析來自請求對象的響應(yīng)時,真正的工作開始了。現(xiàn)在你可以用你請求的數(shù)據(jù)開始工作。僅為測試目的,在開發(fā)期間,可以使用responseText和responseXML屬性來顯示來自響應(yīng)的原始數(shù)據(jù)。為了存取XML響應(yīng)中的結(jié)點,首先使用你創(chuàng)建的請求對象,定位到responseXML屬性以檢索(你可能已經(jīng)猜測出來)來自響應(yīng)的XML。定位到documentElement-它檢索一個到XML響應(yīng)的根結(jié)點的參考。
var response = request.responseXML.documentElement; |
現(xiàn)在既然你有了到響應(yīng)的根結(jié)點的參考,那么你可以使用getElementsByTagName()以結(jié)點名字來檢索childNodes。下面一行用一個頭部的nodeName來定位一個childNode:
response.getElementsByTagName('header')[0].firstChild.data; |
使用firstChild.data可以允許你存取該元素中的文本:
response.getElementsByTagName('header')[0].firstChild.data; |
下面是怎樣創(chuàng)建這些代碼的完整的例子:
var response = request.responseXML.documentElement; var header = response.getElementsByTagName('header')[0].firstChild.data; document.getElementById('copy').innerHTML = header; |
八、 需求分析
現(xiàn)在既然你知道怎樣使用AJAX的基礎(chǔ)知識,那么下一步就是決定是否在一工程使用它。須記住的最重要的事情是,在你還沒有刷新頁面時你無法使用"Back"按鈕。為此,可以先專注于你的工程中的一小部分-它能夠從使用這種類型的交互中受益。例如,你可以創(chuàng)建一個表單-它在用戶每次輸入一個輸入字段或一個字母時查詢一個腳本以便進行實時校驗。你可以創(chuàng)建一個拖放頁面-在釋放一項時,它能夠把數(shù)據(jù)發(fā)送到一個腳本中并把該頁面的狀態(tài)保存到一個數(shù)據(jù)庫中。使用AJAX的理由毫無疑問是存在的;并且這種使用無論對開發(fā)者還是用戶都會帶來益處;這全依賴于具體的條件和執(zhí)行情況。
還有其它方法可用來解決"Back"按鈕的問題,例如使用Google Gmail-它現(xiàn)在能夠為你的操作提供一種撤消功能而不刷新該頁面。以后還會出現(xiàn)許多更具創(chuàng)造性的例子-它們將通過提供給開發(fā)者創(chuàng)建獨特實時的體驗的手段給用戶帶來更大的好處。
九、 結(jié)論
盡管AJAX允許我們構(gòu)建新的和改進的方式來與一個WEB頁面進行交互;但是作為開發(fā)者,我們需要牢記產(chǎn)品是不考慮技術(shù)的;它關(guān)心的是用戶以及其如何與用戶進行交互。沒有了用戶群,我們構(gòu)建的工程毫無用處。基于這個標(biāo)準(zhǔn),我們就能評估應(yīng)該使用什么技術(shù)以及何時使用它們來創(chuàng)建對相應(yīng)用戶有用的應(yīng)用軟件。
————————————————————————————————————————
————————————————————————————————————————