Ajax 由HTML、JavaScript™ 技術、DHTML 和DOM 組成,這項傑出的方法可以將笨拙的Web 介面轉換成互動性的Ajax 應用程式。本文的作者是一位Ajax 專家,他示範了這些技術如何協同工作—— 從總體概述到細節的討論—— 使高效的Web 開發成為現實。他也揭開了Ajax 核心概念的神秘面紗,包括XMLHttpRequest 物件。
五年前,如果您不知道XML,您就是一隻無人重視的醜小鴨。十八個月前,Ruby 成了關注的中心,不知道Ruby 的程式設計師只能坐冷板凳了。今天,如果想跟上最新的科技時尚,那您的目標就是Ajax。
但是,Ajax 不僅僅是一種時尚,它是一種建立網站的強大方法,而且不像學習一種全新的語言那麼困難。
但在詳細探討Ajax 是什麼之前,先讓我們花幾分鐘了解Ajax 做什麼。目前,編寫應用程式時有兩種基本的選擇:
·桌面應用程式·Web 應用程式
兩者是類似的,桌面應用程式通常以CD 為介質(有時可從網站下載)並完全安裝到您的計算機上。桌面應用程式可能使用互聯網下載更新,但運行這些應用程式的程式碼在桌面電腦上。 Web 應用程式運行在某處的Web 伺服器上— 毫不奇怪,要透過Web 瀏覽器存取這種應用程式。
不過,比這些應用程式的運行程式碼放在何處更重要的是,應用程式如何運作以及如何與其互動。桌面應用程式通常很快(就在您的電腦上運行,不用等待互聯網連接),具有漂亮的用戶介面(通常和作業系統有關)和非凡的動態性。可以點擊、選擇、輸入、打開選單和子選單、到處巡遊,基本上不需要等待。
另一方面,Web 應用程式是最新的潮流,它們提供了在桌面上無法實現的服務(例如Amazon.com 和eBay)。但是,伴隨著Web 的強大而出現的是等待,等待伺服器回應,等待螢幕刷新,等待請求返回和產生新的頁面。
顯然這樣說過於簡略了,但基本的概念就是如此。您可能已經猜到,Ajax 嘗試建立桌面應用程式的功能和互動性,與不斷更新的Web 應用程式之間的橋樑。可以使用像桌面應用程式中常見的動態使用者介面和漂亮的控件,不過是在Web 應用程式中。
還等什麼呢?讓我們來看看Ajax 如何將笨拙的Web 介面轉化成能迅速回應的Ajax 應用程式。
老技術,新技巧
在談到Ajax 時,實際上涉及到多種技術,要靈活地運用它必須深入了解這些不同的技術(本系列的頭幾篇文章將分別討論這些技術)。好消息是您可能已經非常熟悉其中的大部分技術,更好的是這些技術都很容易學習,並不像完整的程式語言(如Java 或Ruby)那麼困難。
以下是Ajax 應用程式所用到的基本技術:
·HTML 用於建立Web 表單並確定應用程式其他部分使用的欄位。
·JavaScript 程式碼是執行Ajax 應用程式的核心程式碼,可協助改善與伺服器應用程式的通訊。
·DHTML 或Dynamic HTML,用於動態更新表單。我們將使用div、span 和其他動態HTML 元素來標記HTML。
·文件物件模型DOM 用於(透過JavaScript 程式碼)處理HTML 結構和(某些情況下)伺服器傳回的XML。
Ajax 的定義
順便說一下,Ajax 是Asynchronous JavaScript and XML(以及DHTML 等)的縮寫。這個短語是Adaptive Path 的Jesse James Garrett 發明的(請參閱參考資料),按照Jesse 的解釋,這不是首字母縮寫詞。
我們來進一步分析這些技術的職責。我將在以後的文章中深入討論這些技術,目前只要熟悉這些組件和技術就可以了。對這些程式碼越熟悉,就越容易從對這些技術的零散了解轉變到真正掌握這些技術(同時也真正打開了Web 應用程式開發的大門)。
XMLHttpRequest 物件
要了解的一個物件可能對您來說也是最陌生的,也就是XMLHttpRequest。這是一個JavaScript 對象,建立該對像很簡單,如清單1 所示。
清單1. 建立新的XMLHttpRequest 物件
<script language="javascript" type="text/javascript">
var xmlHttp = new XMLHttpRequest();
</script>
下一期文章將進一步討論這個對象,現在要知道這是處理所有伺服器通訊的對象。在繼續閱讀之前,先停下來想一想:透過XMLHttpRequest 物件與伺服器進行對話的是JavaScript 技術。這不是一般的應用程式流,這正是Ajax 的強大功能的來源。
在一般的Web 應用程式中,使用者填寫表單欄位並點擊Submit 按鈕。然後整個表單傳送到伺服器,伺服器將它轉送給處理表單的腳本(通常是PHP 或Java,也可能是CGI 進程或類似的東西),腳本執行完成後再傳回全新的頁面。該頁面可能是具有已填充某些資料的新表單的HTML,也可能是確認頁面,或是具有根據原始表單中輸入資料選擇的某些選項的頁面。當然,在伺服器上的腳本或程式處理和返回新表單時用戶必須等待。螢幕變成一片空白,等到伺服器回傳資料後再重新繪製。這就是互動性差的原因,用戶得不到立即回饋,因此感覺不同於桌面應用程式。
Ajax 基本上就是把JavaScript 技術和XMLHttpRequest 物件放在Web 表單和伺服器之間。當使用者填寫表單時,資料會傳送給一些JavaScript 程式碼而不是直接傳送給伺服器。相反,JavaScript 程式碼會擷取表單資料並向伺服器發送請求。同時使用者螢幕上的表單也不會閃爍、消失或延遲。換句話說,JavaScript 程式碼在幕後發送請求,使用者甚至不知道請求的發出。更好的是,請求是非同步發送的,就是說JavaScript 程式碼(和使用者)不用等待伺服器的回應。因此用戶可以繼續輸入資料、滾動螢幕和使用應用程式。
然後,伺服器將資料傳回JavaScript 程式碼(仍在Web 表單中),後者決定如何處理這些資料。它可以迅速更新表單數據,讓人感覺應用程式是立即完成的,表單沒有提交或刷新而用戶得到了新數據。 JavaScript 程式碼甚至可以對收到的資料執行某種計算,再發送另一個請求,完全不需要使用者乾預!這就是XMLHttpRequest 的強大之處。它可以根據需要自行與伺服器進行交互,用戶甚至可以完全不知道幕後發生的一切。結果就是類似桌面應用程式的動態、快速反應、高互動性的體驗,但背後又擁有網路的全部強大力量。
加入一些JavaScript
得到XMLHttpRequest 的句柄後,其他的JavaScript 程式碼就非常簡單了。事實上,我們將使用JavaScript 程式碼完成非常基本的任務:
·取得表單資料:JavaScript 程式碼很容易從HTML 表單中抽取資料並傳送到伺服器。
·修改表單上的資料:更新表單也很簡單,從設定欄位值到迅速替換影像。
·解析HTML 和XML:使用JavaScript 程式碼操縱DOM(請參閱下一節),處理HTML 表單伺服器傳回的XML 資料的結構。
對於前兩點,需要非常熟悉getElementById() 方法,如清單2 所示。
清單2. 用JavaScript 程式碼擷取並設定欄位值
// Get the value of the "phone" field and stuff it in a variable called phone
var phone = document.getElementById("phone").value;
// Set some values on a form using an array called response
document.getElementById("order").value = response[0];
document.getElementById("address").value = response[1];
這裡沒有特別需要注意的地方,真是好極了!您應該認識到這裡並沒有非常複雜的東西。只要掌握了XMLHttpRequest,Ajax 應用程式的其他部分就是如清單2 所示的簡單JavaScript 程式碼了,混合有少量的HTML。同時,還要用一點兒DOM,我們就來看看。
以DOM 結束
最後還有DOM,也就是文件物件模型。可能對有些讀者來說DOM 有點令人生畏,HTML 設計者很少使用它,即使JavaScript 程式設計師也不大用到它,除非要完成某項高階程式設計任務。大量使用DOM 的是複雜的Java 和C/C++ 程序,這可能是DOM 被認為難以學習的原因。
幸運的是,在JavaScript 技術中使用DOM 很容易,也非常直覺。現在,按照常規也許應該說明如何使用DOM,或至少要給出一些範例程式碼,但這樣做也可能誤導您。即使不理會DOM,仍然能深入探討Ajax,這也是我準備採用的方法。以後的文章將再次討論DOM,現在只要知道可能需要DOM 就可以了。當需要在JavaScript 程式碼和伺服器之間傳遞XML 和改變HTML 表單的時候,我們再深入研究DOM。沒有它也能做一些有趣的工作,所以現在就把DOM 放到一邊吧。
在取得Request 物件
有了上面的基礎知識後,我們來看看一些具體的範例。 XMLHttpRequest 是Ajax 應用程式的核心,而且對許多讀者來說可能還比較陌生,我們就從這裡開始吧。從清單1 可以看出,建立和使用這個物件非常簡單,不是嗎?等一等。
還記得幾年前的那些討厭的瀏覽器戰爭嗎?沒有一樣東西在不同的瀏覽器上得到相同的結果。不管您是否相信,這些戰爭仍然在繼續,雖然規模較小。但令人奇怪的是,XMLHttpRequest 成了這場戰爭的犧牲品之一。因此取得XMLHttpRequest 物件可能需要採用不同的方法。下面我將詳細地進行解釋。
使用Microsoft 瀏覽器
Microsoft 瀏覽器Internet Explorer 使用MSXML 解析器處理XML(可透過參考資料進一步了解MSXML)。因此如果編寫的Ajax 應用程式要和Internet Explorer 打交道,那麼必須用一種特殊的方式建立物件。
但並不是這麼簡單。根據Internet Explorer 中安裝的JavaScript 技術版本不同,MSXML 實際上有兩種不同的版本,因此必須對這兩種情況分別編寫程式碼。請參閱清單3,其中的程式碼在Microsoft 瀏覽器上建立了一個XMLHttpRequest。
清單3. 在Microsoft 瀏覽器上建立XMLHttpRequest 物件
var xmlHttp = false;
try {
xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e2) {
xmlHttp = false;
}
}
您對這些程式碼可能還不完全理解,但沒有關係。當本系列文章結束的時候,您將對JavaScript 程式設計、錯誤處理、條件編譯等有更深的了解。現在只要牢牢記住其中的兩行程式碼:
xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
和
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");。
這兩行程式碼基本上就是嘗試使用一個版本的MSXML 建立對象,如果失敗則使用另一個版本建立該對象。不錯吧?如果都不成功,則將xmlHttp 變數設為false,告訴您的程式碼出現了問題。如果發生這種情況,可能是因為安裝了非Microsoft 瀏覽器,則需要使用不同的程式碼。
處理Mozilla 和非Microsoft 瀏覽器
如果選擇的瀏覽器不是Internet Explorer,或為非Microsoft 瀏覽器編寫程式碼,就需要使用不同的程式碼。事實上就是清單1 所示的一行簡單程式碼:
var xmlHttp = new XMLHttpRequest object;。
這行簡單得多的程式碼在Mozilla、Firefox、Safari、Opera 以及基本上所有以任何形式或方式支援Ajax 的非Microsoft 瀏覽器中,創建了XMLHttpRequest 物件。
結合起來
關鍵是要支援所有瀏覽器。誰願意編寫一個只能用於Internet Explorer 或非Microsoft 瀏覽器的應用程式呢?或者更糟,要寫一個應用程式兩次?當然不!因此程式碼要同時支援Internet Explorer 和非Microsoft 瀏覽器。清單4 顯示了這樣的程式碼。
清單4. 以支援多種瀏覽器的方式建立XMLHttpRequest 對象
/* Create a new XMLHttpRequest object to talk to the Web server */
var xmlHttp = false;
/*@cc_on @*/
/*@if (@_jscript_version >= 5)
try {
xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e2) {
xmlHttp = false;
}
}
@end @*/
if (!xmlHttp && typeof XMLHttpRequest != 'undefined') {
xmlHttp = new XMLHttpRequest();
}
現在先不管那些註解掉的奇怪符號,如@cc_on,這是特殊的JavaScript 編譯器指令,將在下一期針對XMLHttpRequest 的文章中詳細討論。這段程式碼的核心分為三個步驟:
1.建立一個變數xmlHttp 來引用即將建立的XMLHttpRequest 物件。
2、嘗試在Microsoft 瀏覽器中建立該物件:
1)嘗試使用Msxml2.XMLHTTP 物件建立它。
2)如果失敗,再嘗試Microsoft.XMLHTTP 物件。
2.如果仍然沒有建立xmlHttp,則以非Microsoft 的方式建立該物件。
最後,xmlHttp 應該引用一個有效的XMLHttpRequest 對象,無論運行什麼樣的瀏覽器。
關於安全性的一點說明
安全性如何呢?現在瀏覽器允許使用者提高他們的安全等級,關閉JavaScript 技術,並停用瀏覽器中的任何選項。在這種情況下,程式碼無論如何都不會運作。此時必須適當地處理問題,這需要單獨的一篇文章來討論,要放到以後了(這個系列夠長了吧?不用擔心,讀完之前也許您就掌握了)。現在要寫一段健壯但不夠完美的程式碼,對於掌握Ajax 來說就很好了。以後我們還將討論更多的細節。
Ajax 世界中的請求/回應
現在我們介紹了Ajax,對XMLHttpRequest 物件以及如何建立它也有了基本的了解。如果閱讀得很仔細,您可能已經知道與伺服器上的Web 應用程式打交道的是JavaScript 技術,而不是直接提交給該應用程式的HTML 表單。
還缺什麼呢?到底如何使用XMLHttpRequest。因為這段程式碼非常重要,您編寫的每個Ajax 應用程式都要以某種形式使用它,先看看Ajax 的基本請求/回應模型是什麼樣子。
發出請求
您已經有了一個嶄新的XMLHttpRequest 對象,現在就讓它幹點活兒吧。首先需要一個Web 頁面能夠呼叫的JavaScript 方法(例如當使用者輸入文字或從選單中選擇一項時)。接下來就是在所有Ajax 應用程式中基本上都雷同的流程:
1、從Web 表單中取得所需的資料。
2、建立要連接的URL。
3.打開到伺服器的連線。
4.設定伺服器在完成後要執行的函數。
5、發送請求。
清單5 中的範例Ajax 方法就是按照這個順序組織的:
清單5. 發出Ajax 請求
function callServer() {
// Get the city and state from the web form
var city = document.getElementById("city").value;
var state = document.getElementById("state").value;
// Only go on if there are values for both fields
if ((city == null) || (city == "")) return;
if ((state == null) || (state == "")) return;
// Build the URL to connect to
var url = "/scripts/getZipCode.php?city=" + escape(city) + "&state=" + escape(state);
// Open a connection to the server
xmlHttp.open("GET", url, true);
// Setup a function for the server to run when it's done
xmlHttp.onreadystatechange = updatePage;
// Send the request
xmlHttp.send(null);
}
其中大部分程式碼意義都很明確。開始的程式碼使用基本JavaScript 程式碼取得幾個表單欄位的值。然後設定一個PHP 腳本作為連結的目標。若要注意腳本URL 的指定方式,city 和state(來自表單)使用簡單的GET 參數附加在URL 之後。
然後打開一個連接,這是您第一次看到使用XMLHttpRequest。其中指定了連接方法(GET)和要連接的URL。最後一個參數如果設為true,那麼將請求一個非同步連接(這就是Ajax 的由來)。如果使用false,那麼程式碼發出請求後將等待伺服器傳回的回應。如果設為true,當伺服器在背景處理請求的時候使用者仍然可以使用表單(甚至呼叫其他JavaScript 方法)。
xmlHttp(要記住,這是XMLHttpRequest 物件實例)的onreadystatechange 屬性可以告訴伺服器在運行完成後(可能要用五分鐘或五個小時)做什麼。因為程式碼沒有等待伺服器,必須讓伺服器知道怎麼做以便您能回應。在這個範例中,如果伺服器處理完了請求,一個特殊的名為updatePage() 的方法將會被觸發。
最後,使用值null 呼叫send()。因為已經在請求URL 中新增了要傳送給伺服器的資料(city 和state),所以請求中不需要傳送任何資料。這樣就發出了請求,伺服器按照您的要求工作。
如果沒有發現任何新鮮的東西,您應該體會到這是多麼簡單明了!除了牢牢記住Ajax 的非同步特性外,這些內容都相當簡單。應該感激Ajax 使您能夠專心編寫漂亮的應用程式和介面,而不用擔心複雜的HTTP 請求/回應程式碼。
清單5 中的程式碼說明了Ajax 的易用性。數據是簡單的文本,可以作為請求URL 的一部分。用GET 而不是更複雜的POST 發送請求。沒有XML 和要添加的內容頭部,請求體中沒有要發送的資料;換句話說,這就是Ajax 的烏托邦。
不用擔心,隨著本系列文章的展開,事情會變得越來越複雜。您將看到如何發送POST 請求、如何設定請求頭部和內容類型、如何在訊息中編碼XML、如何增加請求的安全性,可以做的工作還有很多!暫時先不用管那些困難,掌握好基本的東西就行了,很快我們就會建立一整套的Ajax 工具庫。
處理回應
現在要面對伺服器的回應了。現在只要知道兩點:
·什麼也不要做,直到xmlHttp.readyState 屬性的值等於4。
·伺服器將把回應填入xmlHttp.responseText 屬性中。
其中的第一點,即就緒狀態,將在下一篇文章中詳細討論,您將進一步了解HTTP 請求的階段,可能比您設想的還要多。現在只要檢查一個特定的值(4)就可以了(下一期文章中還有更多的值要介紹)。第二點,使用xmlHttp.responseText 屬性來獲得伺服器的回應,這很簡單。清單6 中的範例方法可供伺服器根據清單5 中傳送的資料呼叫。
清單6. 處理伺服器回應
function updatePage() {
if (xmlHttp.readyState == 4) {
var response = xmlHttp.responseText;
document.getElementById("zipCode").value = response;
}
}
這些程式碼同樣既不難也不複雜。它等待伺服器調用,如果是就緒狀態,則使用伺服器返回的值(這裡是使用者輸入的城市和州的ZIP 編碼)設定另一個表單欄位的值。於是包含ZIP 編碼的zipCode 欄位突然出現了,而使用者沒有按任何按鈕!這就是前面所說的桌面應用程式的感覺。快速回應、動態感受等等,這些都只因為有了小小的一段Ajax 程式碼。
細心的讀者可能注意到zipCode 是一個普通的文字欄位。一旦伺服器回傳ZIP 編碼,updatePage() 方法就用城市/州的ZIP 編碼設定那個欄位的值,使用者就可以改寫該值。這樣做有兩個原因:保持例子簡單,說明有時候可能希望使用者能夠修改伺服器回傳的資料。要記住這兩點,它們對於好的使用者介面設計很重要。
連結Web 表單
還有什麼呢?實際上沒有多少了。一個JavaScript 方法會捕捉使用者輸入表單的資訊並將其傳送到伺服器,另一個JavaScript 方法監聽和處理回應,並在回應返回時設定欄位的值。所有這些實際上都依賴於呼叫第一個JavaScript 方法,它啟動了整個過程。最明顯的辦法是在HTML 表單中增加一個按鈕,但這是2001 年的辦法,您不這麼認為嗎?還是像清單7 那樣利用JavaScript 技術吧。
清單7. 啟動一個Ajax 流程
<form>
<p>City: <input type="text" name="city" id="city" size="25"
onChange="callServer();" /></p>
<p>State: <input type="text" name="state" id="state" size="25"
onChange="callServer();" /></p>
<p>Zip Code: <input type="text" name="zipCode" id="city" size="5" /></p>
</form>
如果感覺這像是一段相當普通的程式碼,那就對了,正是如此!當使用者在city 或state 欄位中輸入新的值時,callServer() 方法就被觸發,於是Ajax 開始運作了。有點兒明白怎麼回事了吧?好,就是如此!
結束語
現在您可能已經準備開始編寫第一個Ajax 應用程式了,至少也希望認真閱讀參考資料中的那些文章了吧?但可以先從這些應用程式如何運作的基本概念開始,對XMLHttpRequest 物件有基本的了解。在下一期文章中,您將掌握這個對象,學會如何處理JavaScript 和伺服器的通訊、如何使用HTML 表單以及如何取得DOM 句柄。
現在先花點時間考慮考慮Ajax 應用程式有多強大。設想一下,當點擊按鈕、輸入一個欄位、從組合方塊中選擇一個選項或用滑鼠在螢幕上拖曳時,Web 表單能夠立刻作出回應會是什麼情形。想想非同步究竟意味著什麼,想想JavaScript 程式碼運作而且不等待伺服器對它的請求作出回應。會遇到什麼樣的問題?會進入什麼樣的領域?考慮到這種新的方法,程式設計的時候該如何改變表單的設計?
如果在這些問題上花一點兒時間,與簡單地剪切/粘貼某些代碼到您根本不理解的應用程序中相比,收益會更多。在下一期文章中,我們將把這些概念付諸實踐,詳細介紹使應用程式按照這種方式工作所需的程式碼。因此,現在先享受一下Ajax 所帶來的可能性吧。