在Web客戶端使用xmlhttp對象,可以十分方便的和服務器交換數據,我們可以獲取和發送任何類型的數據,甚至二進制數據到服務器上。xmlhttp技術同時也是目前大多數無刷新頁面使用的和服務器交換數據的方式,這種方式比以往的隱藏iframe的方法要方便和經濟的多。
的標準,不過IE, Netscape/Mozilla, 和Safari都支持。在IE中我們使用new ActiveXObject('MSXML2.XMLHTTP')或者new ActiveXObject("Microsoft.XMLHTTP")來獲得的xmlhttp對象實例,使用前者還是后者和客戶端機器安裝的MSXML版本有關。在Netscape/Mozilla和Safari中,使用new XMLHttpRequest()來獲得xmlhttp對象實例。比如在IE中,我們通常這樣使用:
我們已經討論了動態Web應用的發展歷史,并簡要介紹了Ajax,下面再來討論問題的關鍵:如何使用XMLHttpRequest對象。盡管與其說Ajax是一種技術,不如說是一種技巧,但如果沒有對XMLHttpRequest的廣泛支持,Google Suggest和Ta-da List可能不會像我們看到的有今天這樣的發展,而你可能也不會看到手上的這本書!
XMLHttpRequest最早是在IE 5中以ActiveX組件形式實現的。由于只能在IE中使用,所以大多數開發人員都沒有用XMLHttpRequest,直到最近,Mozilla 1.0和Safari 1.2把它采用為事實上的標準,情況才有改觀。需要重點說明的是,XMLHttpRequest并不是一個W3C標準,不過許多功能已經涵蓋在一個新提案中:DOM Level 3加載和保存規約(DOM Level 3 Load and Save Specification)。因為它不是標準,所以在不同瀏覽器上的表現也稍有區別,不過大多數方法和屬性都得到了廣泛的支持。當前,Firefox、Safari、Opera、Konqueror和Internet Explorer都以類似的方式實現了XMLHttpRequest對象的行為。
前面已經說過,如果大量用戶還是在使用較舊的瀏覽器訪問網站或應用,就要三思了。第1章討論過,在這種情況下,如果要使用Ajax技術,要么需要開發一個候選網站,要么你的應用應當能妥善地降級。大多數使用統計表明,在當前使用的瀏覽器中只有極少數不支持XMLHttpRequest,所以一般情況下不會存在這個問題。不過,還是應該查看Web日志,確定你的用戶在使用什么樣的客戶端來訪問網站。
2.1 XMLHttpRequest對象概述
在使用XMLHttpRequest對象發送請求和處理響應之前,必須先用JavaScript創建一個XMLHttpRequest對象。由于XMLHttpRequest不是一個W3C標準,所以可以采用多種方法使用JavaScript來創建XMLHttpRequest的實例。Internet Explorer把XMLHttpRequest實現為一個ActiveX對象,其他瀏覽器(如Firefox、Safari和Opera)把它實現為一個本地JavaScript對象。由于存在這些差別,JavaScript代碼中必須包含有關的邏輯,從而使用ActiveX技術或者使用本地JavaScript對象技術來創建XMLHttpRequest的一個實例。
很多人可能還記得從前的那段日子,那時不同瀏覽器上的JavaScript和DOM實現簡直千差萬別,聽了上面這段話之后,這些人可能又會不寒而栗。幸運的是,在這里為了明確該如何創建XMLHttpRequest對象的實例,并不需要那么詳細地編寫代碼來區別瀏覽器類型。你要做的只是檢查瀏覽器是否提供對ActiveX對象的支持。如果瀏覽器支持ActiveX對象,就可以使用ActiveX來創建XMLHttpRequest對象。否則,就要使用本地JavaScript對象技術來創建。代碼清單2-1展示了編寫跨瀏覽器的JavaScript代碼來創建XMLHttpRequest對象實例是多么簡單。
代碼清單2-1 創建XMLHttpRequest對象的一個實例
var xmlHttp;
function createXMLHttpRequest() {
?if (window.ActiveXObject) {
??xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
?}
?else if (window.XMLHttpRequest) {
??xmlHttp = new XMLHttpRequest();
?}
}
可以看到,創建XMLHttpRequest對象相當容易。首先,要創建一個全局作用域變量xmlHttp來保存這個對象的引用。createXMLHttpRequest方法完成創建XMLHttpRequest實例的具體工作。這個方法中只有簡單的分支邏輯(選擇邏輯)來確定如何創建對象。對window.ActiveXObject的調用會返回一個對象,也可能返回null,if語句會把調用返回的結果看作是true或false(如果返回對象則為true,返回null則為false),以此指示瀏覽器是否支持ActiveX控件,相應地得知瀏覽器是不是Internet Explorer。如果確實是,則通過實例化ActiveXObject的一個新實例來創建XMLHttpRequest對象,并傳入一個串指示要創建何種類型的ActiveX對象。在這個例子中,為構造函數提供的字符串是Microsoft.XMLHTTP,這說明你想創建XMLHttpRequest的一個實例。
如果window.ActiveXObject調用失敗(返回null),JavaScript就會轉到else語句分支,確定瀏覽器是否把XMLHttpRequest實現為一個本地JavaScript對象。如果存在window.
XMLHttpRequest,就會創建XMLHttpRequest的一個實例。
由于JavaScript具有動態類型特性,而且XMLHttpRequest在不同瀏覽器上的實現是兼容的,所以可以用同樣的方式訪問XMLHttpRequest實例的屬性和方法,而不論這個實例創建的方法是什么。這就大大簡化了開發過程,而且在JavaScript中也不必編寫特定于瀏覽器的邏輯。
2.2 方法和屬性
表2-1顯示了XMLHttpRequest對象的一些典型方法。不要擔心,稍后就會詳細介紹這些方法。
表2-1 標準XMLHttpRequest操作
方 法?描 述?
abort()?停止當前請求?
getAllResponseHeaders()?把HTTP請求的所有響應首部作為鍵/值對返回?
getResponseHeader("header")?返回指定首部的串值?
open("method", "url")?建立對服務器的調用。method參數可以是GET、POST或PUT。url參數可以是相對URL或絕對URL。這個方法還包括3個可選的參數?
send(content)?向服務器發送請求?
setRequestHeader("header", "value")?把指定首部設置為所提供的值。在設置任何首部之前必須先調用open()?
下面來更詳細地討論這些方法。
void open(string method, string url, boolean asynch, string username, string password):這個方法會建立對服務器的調用。這是初始化一個請求的純腳本方法。它有兩個必要的參數,還有3個可選參數。要提供調用的特定方法(GET、POST或PUT),還要提供所調用資源的URL。另外還可以傳遞一個Boolean值,指示這個調用是異步的還是同步的。默認值為true,表示請求本質上是異步的。如果這個參數為false,處理就會等待,直到從服務器返回響應為止。由于異步調用是使用Ajax的主要優勢之一,所以倘若將這個參數設置為false,從某種程度上講與使用XMLHttpRequest對象的初衷不太相符。不過,前面已經說過,在某些情況下這個參數設置為false也是有用的,比如在持久存儲頁面之前可以先驗證用戶的輸入。最后兩個參數不說自明,允許你指定一個特定的用戶名和密碼。
void send(content):這個方法具體向服務器發出請求。如果請求聲明為異步的,這個方法就會立即返回,否則它會等待直到接收到響應為止。可選參數可以是DOM對象的實例、輸入流,或者串。傳入這個方法的內容會作為請求體的一部分發送。
void setRequestHeader(string header, string value):這個方法為HTTP請求中一個給定的首部設置值。它有兩個參數,第一個串表示要設置的首部,第二個串表示要在首部中放置的值。需要說明,這個方法必須在調用open()之后才能調用。
在所有這些方法中,最有可能用到的就是open()和send()。XMLHttpRequest對象還有許多屬性,在設計Ajax交互時這些屬性非常有用。
void abort():顧名思義,這個方法就是要停止請求。
string getAllResponseHeaders():這個方法的核心功能對Web應用開發人員應該很熟悉了,它返回一個串,其中包含HTTP請求的所有響應首部,首部包括Content-
Length、Date和URI。
string getResponseHeader(string header):這個方法與getAllResponseHeaders()是對應的,不過它有一個參數表示你希望得到的指定首部值,并且把這個值作為串返回。
除了這些標準方法,XMLHttpRequest對象還提供了許多屬性,如表2-2所示。處理XMLHttpRequest時可以大量使用這些屬性。
表2-2 標準XMLHttpRequest屬性
屬 性?描 述?
onreadystatechange?每個狀態改變時都會觸發這個事件處理器,通常會調用一個JavaScript函數?
readyState?請求的狀態。有5個可取值:0 = 未初始化,1 = 正在加載,2 = 已加載,3 = 交互中,4 = 完成?
responseText?服務器的響應,表示為一個串?
responseXML?服務器的響應,表示為XML。這個對象可以解析為一個DOM對象?
status?服務器的HTTP狀態碼(200對應OK,404對應Not Found(未找到),等等)?
statusText?HTTP狀態碼的相應文本(OK或Not Found(未找到)等等)?
2.3 交互示例
看到這里,你可能想知道典型的Ajax交互是什么樣。圖2-1顯示了Ajax應用中標準的交互模式。
圖2-1 標準Ajax交互
不同于標準Web客戶中所用的標準請求/響應方法,Ajax應用的做法稍有差別。
1.?一個客戶端事件觸發一個Ajax事件。從簡單的onchange事件到某個特定的用戶動作,很多這樣的事件都可以觸發Ajax事件。可以有如下的代碼:
<input type="text"d="email" name="email" onblur="validateEmail()";>
2.?創建XMLHttpRequest對象的一個實例。使用open()方法建立調用,并設置URL以及所希望的HTTP方法(通常是GET或POST)。請求實際上通過一個send()方法調用觸發。可能的代碼如下所示:
var xmlHttp;
function validateEmail() {
var email = document.getElementById("email");
var url = "validate?email=" + escape(email.value);
if (window.ActiveXObject) {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
else if (window.XMLHttpRequest) {
xmlHttp = new XMLHttpRequest();
}
xmlHttp.open("GET", url);
xmlHttp.onreadystatechange = callback;
xmlHttp.send(null);
}
3.?向服務器做出請求。可能調用servlet、CGI腳本,或者任何服務器端技術。
4.?服務器可以做你想做的事情,包括訪問數據庫,甚至訪問另一個系統。
5.?請求返回到瀏覽器。Content-Type設置為text/xml——XMLHttpRequest對象只能處理text/html類型的結果。在另外一些更復雜示例中,響應可能涉及更廣,還包括JavaScript、DOM管理以及其他相關的技術。需要說明,你還需要設置另外一些首部,使瀏覽器不會在本地緩存結果。為此可以使用下面的代碼:
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
6.?在這個示例中,XMLHttpRequest對象配置為處理返回時要調用callback()函數。這個函數會檢查XMLHttpRequest對象的readyState屬性,然后查看服務器返回的狀態碼。如果一切正常,callback()函數就會在客戶端上做些有意思的工作。以下就是一個典型的回調方法:
function callback() {
if (xmlHttp.readyState == 4) {
if (xmlHttp.status == 200) {
//do something interesting here
}
}
}
可以看到,這與正常的請求/響應模式有所不同,但對Web開發人員來說,并不是完全陌生的。顯然,在創建和建立XMLHttpRequest對象時還可以做些事情,另外當“回調”函數完成了狀態檢查之后也可以有所作為。一般地,你會把這些標準調用包裝在一個庫中,以便在整個應用中使用,或者可以使用Web上提供的庫。這個領域還很新,但是在開源社區中已經如火如荼地展開了大量的工作。
通常,Web上提供的各種框架和工具包負責基本的連接和瀏覽器抽象,有些還增加了用戶界面組件。有一些純粹基于客戶,還有一些需要在服務器上工作。這些框架中的很多只是剛開始開發,或者還處于發布的早期階段,隨著新的庫和新的版本的定期出現,情況還在不斷發生變化。這個領域正在日漸成熟,最具優勢的將脫穎而出。一些比較成熟的庫包括libXmlRequest、RSLite、sarissa、JavaScript對象注解(JavaScript Object Notation,JSON)、JSRS、直接Web遠程通信(Direct Web Remoting,DWR)和Rails on Ruby。這個領域日新月異,所以應當適當地配置你的RSS收集器,及時收集有關Ajax的所有網站上的信息!
2.4 GET與POST
你可能想了解GET和POST之間有什么區別,并想知道什么時候使用它們。從理論上講,如果請求是冪等的就可以使用GET,所謂冪等是指多個請求返回相同的結果。實際上,相應的服務器方法可能會以某種方式修改狀態,所以一般情況下這是不成立的。這只是一種標準。更實際的區別在于凈荷的大小,在許多情況下,瀏覽器和服務器會限制URL的長度URL用于向服務器發送數據。一般來講,可以使用GET從服務器獲取數據;換句話說,要避免使用GET調用改變服務器上的狀態。
一般地,當改變服務器上的狀態時應當使用POST方法。不同于GET,需要設置XML- HttpRequest對象的Content-Type首部,如下所示:
xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
與GET不同,POST不會限制發送給服務器的凈荷的大小,而且POST請求不能保證是冪等的。
你做的大多數請求可能都是GET請求,不過,如果需要,也完全可以使用POST。
2.5 遠程腳本
我們已經介紹了Ajax,下面來簡單談談遠程腳本。你可能會想:“Ajax有什么大不了的?我早就用IFRAME做過同樣的事情。”實際上,我們自己也曾用過這種方法。這在以前一般稱為遠程腳本(remote scripting),很多人認為這只是一種修修補補。不過,這確實提供了一種能避免頁面刷新的機制。
2.5.1 遠程腳本概述
基本說來,遠程腳本是一種遠程過程調用類型。你可以像正常的Web應用一樣與服務器交互,但是不用刷新整個頁面。與Ajax類似,你可以調用任何服務器端技術來接收請求、處理請求并返回一個有意義的結果。正如在服務器端有很多選擇,客戶端同樣有許多實現遠程腳本的選擇。你可以在應用中嵌入Flash動畫、Java applet,或者ActiveX組件,甚至可以使用XML-RPC,但是這種方法過于復雜,因此除非你使用這種技術很有經驗,否則這種方法不太合適。實現遠程腳本的通常做法包括將腳本與一個IFRAME(隱藏或不隱藏)結合,以及由服務器返回JavaScript,然后再在瀏覽器中運行這個JavaScript。
Microsoft提供了自己的遠程腳本解決方案,并聰明地稱之為Microsoft遠程腳本(Microsoft Remote Scripting,MSRS)。采用這種方法,可以像調用本地腳本一樣調用服務器腳本。頁面中嵌入Java applet,以便與服務器通信,.asp頁面用于放置服務器端腳本,并用.htm文件管理客戶端的布局擺放。在Netscape和IE 4.0及更高版本中都可以使用Microsoft的這種解決方案,可以同步調用,也可以異步調用。不過,這種解決方案需要Java,這意味著可能還需要附加的安裝例程,而且還需要Internet Information Services(IIS),因此會限制服務器端的選擇。
Brent Ashley為遠程腳本創建了兩個免費的跨平臺庫。JSRS是一個客戶端JavaScript庫,它充分利用DHTML向服務器做遠程調用。相當多的操作系統和瀏覽器上都能使用JSRS。如果采用一些常用的、流行的服務器端實現(如PHP、Python和Perl CGI),JSRS一般都能在網站上安裝并運行。Ashley免費提供了JSRS,而且還可以從他的網站(www.ashleyit.com
/rs/main.htm)上得到源代碼。
如果你覺得JSRS太過笨重,Ashley還創建了RSLite,這個庫使用了cookie。RSLite僅限于少量數據和單一調用,不過大多數瀏覽器都能提供支持。
2.5.2 遠程腳本的示例
為了進行比較,這里向你展示如何使用IFRAME來實現類似Ajax的技術。這非常簡單,而且過去我們就用過這種方法(在XMLHttpRequest問世之前)。這個示例并沒有真正調用服務器,只是想讓你對如何使用IFRAME實現遠程腳本有所認識。
這個示例包括兩個文件:iframe.html(見代碼清單2-2)和server.html(見代碼清單2-3)。server.html模擬了本應從服務器返回的響應。
代碼清單2-2 iframe.html文件
<html>
<head>
<title>Example of remote scripting in an IFRAME</title>
</head>
<script type="text/javascript">
function handleResponse() {
alert('this function is called from server.html');
}
</script>
<body>
<h1>Remote Scripting with an IFRAME</h1>
<iframe id="beforexhr"
name="beforexhr"
style="width:0px; height:0px; border: 0px"
src="blank.html"></iframe>
<a href="server.html" target="beforexhr">call the server</a>
</body>
</html>
代碼清單2-3 server.html文件
<html>
<head>
<title>the server</title>
</head>
<script type="text/javascript">
window.parent.handleResponse();
</script>
<body>
</body>
</html>
圖2-2顯示了最初的頁面。運行這個代碼生成的結果如圖2-3所示。
圖2-2 最初的頁面
圖2-3 調用“服務器”之后的頁面
2.6 如何發送簡單請求
現在已經準備開始使用XMLHttpRequest對象了。我們剛剛討論了如何創建這個對象,下面來看如何向服務器發送請求,以及如何處理服務器的響應。
最簡單的請求是,不以查詢參數或提交表單數據的形式向服務器發送任何信息。在實際中,往往都希望向服務器發送一些信息。
使用XMLHttpRequest對象發送請求的基本步驟如下:
1.?為得到XMLHttpRequest對象實例的一個引用,可以創建一個新的實例,也可以訪問包含有XMLHttpRequest實例的一個變量。
2.?告訴XMLHttpRequest對象,哪個函數會處理XMLHttpRequest對象狀態的改變,為此要把對象的onreadystatechange屬性設置為指向JavaScript函數的指針。
3.?指定請求的屬性。XMLHttpRequest對象的open()方法會指定將發出的請求。open()方法取3個參數:一個是指示所用方法(通常是GET或POST)的串;一個是表示目標資源URL的串;一個是Boolean值,指示請求是否是異步的。
4.?將請求發送給服務器。XMLHttpRequest對象的send()方法把請求發送到指定的目標資源。send()方法接受一個參數,通常是一個串或一個DOM對象。這個參數作為請求體的一部分發送到目標URL。當向send()方法提供參數時,要確保open()中指定的方法是POST。如果沒有數據作為請求體的一部分被發送,則使用null。
這些步驟很直觀:你需要XMLHttpRequest對象的一個實例,要告訴它如果狀態有變化該怎么做,還要告訴它向哪里發送請求以及如何發送請求,最后還需要指導XMLHttpRequest發送請求。不過,除非你對C或C++很了解,否則可能不明白函數指針(function pointer)是什么意思。
函數指針與任何其他變量類似,只不過它指向的不是像串、數字、甚至對象實例之類的數據,而是指向一個函數。在JavaScript中,所有函數在內存中都編有地址,可以使用函數名引用。這就提供了很大的靈活性,可以把函數指針作為參數傳遞給其他函數,或者在一個對象的屬性中存儲函數指針。
對于XMLHttpRequest對象,onreadystatechange屬性存儲了回調函數的指針。當XMLHttpRequest對象的內部狀態發生變化時,就會調用這個回調函數。當進行了異步調用,請求就會發出,腳本立即繼續處理(在腳本繼續工作之前,不必等待請求結束)。一旦發出了請求,對象的readyState屬性會經過幾個變化。盡管針對任何狀態都可以做一些處理,不過你最感興趣的狀態可能是服務器響應結束時的狀態。通過設置回調函數,就可以有效地告訴XMLHttpRequest對象:“只要響應到來,就調用這個函數來處理響應。”
2.6.1 簡單請求的示例
第一個示例很簡單。這是一個很小的HTML頁面,只有一個按鈕。點擊這個按鈕會初始化一個發至服務器的異步請求。服務器將發回一個簡單的靜態文本文件作為響應。在處理這個響應時,會在一個警告窗口中顯示該靜態文本文件的內容。代碼清單2-4顯示了這個HTML頁面和相關的JavaScript。
代碼清單2-4 simpleRequest.html頁面
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Simple XMLHttpRequest</title>
<script type="text/javascript">
var xmlHttp;
function createXMLHttpRequest() {
?if (window.ActiveXObject) {
??xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
?}
?else if (window.XMLHttpRequest) {
??xmlHttp = new XMLHttpRequest();
?}
}
function startRequest() {
?createXMLHttpRequest();
?xmlHttp.onreadystatechange = handleStateChange;
?xmlHttp.open("GET", "simpleResponse.xml", true);
?xmlHttp.send(null);
}
function handleStateChange() {
?if(xmlHttp.readyState == 4) {
??if(xmlHttp.status == 200) {
???alert("The server replied with: " + xmlHttp.responseText);
??}
?}
}
</script>
</head>
<body>
?<form action="#">
??<input type="button" value="Start Basic Asynchronous Request"
????onclick="startRequest();"/>
?</form>
</body>
</html>
服務器的響應文件simpleResponse.xml只有一行文本。點擊HTML頁面上的按鈕會生成一個警告框,其中顯示simpleResponse.xml文件的內容。在圖2-4中可以看到分別在Internet Explorer和Firefox中顯示的包含服務器響應的相同警告框。
對服務器的請求是異步發送的,因此瀏覽器可以繼續響應用戶輸入,同時在后臺等待服務器的響應。如果選擇同步操作,而且倘若服務器的響應要花幾秒才能到達,瀏覽器就會表現得很遲鈍,在等待期間不能響應用戶的輸入。這樣一來,瀏覽器好像被凍住一樣,無法響應用戶輸入,而異步做法可以避免這種情況,從而讓最終用戶有更好的體驗。盡管這種改善很細微,但確實很有意義。這樣用戶就能繼續工作,而且服務器會在后臺處理先前的請求。
圖2-4 第一個簡單的異步請求
與服務器通信而不打斷用戶的使用流程,這種能力使開發人員采用多種技術改善用戶體驗成為可能。例如,假設有一個驗證用戶輸入的應用。用戶在輸入表單上填寫各個字段時,瀏覽器可以定期地向服務器發送表單值來進行驗證,此時并不打斷用戶,他還可以繼續填寫余下的表單字段。如果某個驗證規則失敗,在表單真正發送到服務器進行處理之前,用戶就會立即得到通知,這就能大大節省用戶的時間,也能減輕服務器上的負載,因為不必在表單提交不成功時完全重建表單的內容。
2.6.2 關于安全
如果討論基于瀏覽器的技術時沒有提到安全,那么討論就是不完整的。XMLHttpRequest對象要受制于瀏覽器的安全“沙箱”。XMLHttpRequest對象請求的所有資源都必須與調用腳本在同一個域內。這個安全限制使得XMLHttpRequest對象不能請求腳本所在域之外的資源。
這個安全限制的強度因瀏覽器而異(見圖2-5)。IE會顯示一個警告,指出可能存在一個潛在的安全風險,但是用戶可以選擇是否繼續發出請求。Firefox則會斷然停止請求,并在JavaScript控制臺顯示一個錯誤消息。
Firefox確實提供了一些JavaScript技巧,使得XMLHttpRequest也可以請求外部URL的資源。不過,由于這些技術針對特定的瀏覽器,所以最好不要用,而且要避免使用XMLHttpRequest訪問外部URL。
2.7 DOM Level 3 加載和保存規約
到目前為止,我們討論的解決方案都不是標準。盡管XMLHttpRequest得到了廣泛支持,但是你已經看到了,創建XMLHttpRequest對象的過程會隨瀏覽器不同而有所差異。許多人錯誤地認為Ajax得到了W3C的支持,但實際上并非如此。W3C在一個新標準中解決了這一問題以及其他缺點,這個標準的名字相當長:DOM Level 3 加載和保存規約。這個規約的設計目的是以一種獨立于平臺和語言的方式,用XML內容修改DOM文檔的內容。2004年4月提出了1.0版本,但到目前為止,還沒有瀏覽器實現這個規約。
什么時候加載和保存規約能取代Ajax?誰也不知道。想想看有多少瀏覽器沒有完全支持現有的標準,所以這很難說,但是隨著越來越多的網站和應用利用了Ajax技術,可能以后的版本會得到支持。不過,較早的DOM版本就花了很長時間才得到采納,所以你得耐心一點。在一次訪談中,DOM Activity主席Philippe Le Hégaret稱,需要花“相當長的時間”才能得到廣泛采納。DOM Level 3也得到了一些支持,Opera的XMLHttpRequest實現就基于DOM Level 3,而且Java XML處理API(Java API for XML Processing,JAXP)1.3版本也支持DOM Level 3。不過,從出現了相應的W3C規約這一點來看,起碼可以表明Ajax技術的重要性。
從1997年8月起,人們就一直在為解決瀏覽器之間的不兼容而努力,加載和保存規約則達到了極致。你可能注意到,標題里寫的是“Level 3”,那么Level 1和Level 2呢?Level 1在1998年10月完成,為我們帶來了HTML 4.0和XML 1.0。如今,Level 1已經得到了廣泛支持。2000年11月,Level 2完成,不過它被采納得比較慢。CSS就是Level 2的一部分。
開發人員能從加載和保存規約得到些什么?在理想情況下,它能解決我們目前遇到的許多跨瀏覽器問題。盡管Ajax很簡單,但是你應該記得,僅僅是為了創建XMLHttpRequest對象的一個實例,就需要檢查瀏覽器的類型。真正的W3C規約可以減少這種編寫代碼的工作。基本說來,加載和保存規約會為Web開發人員提供一個公共的API,可以以一種獨立于平臺和語言的方式來訪問和修改DOM。換句話說,不論你的平臺是Windows還是Linux,也不論你用VBScript開發還是用JavaScript開發,都沒有關系。還可以把DOM樹保存為一個XML文檔,或者將一個XML文檔加載到DOM。另外,規約還提供了對XML 1.1、XML Schema 1.0和SOAP 1.2的支持。這個規約很可能得到開發人員的廣泛使用。
2.8 DOM
我們一直在說DOM,如果你沒有做過太多客戶端的工作,可能不知道什么是DOM。DOM是一個W3C規約,可以以一種獨立于平臺和語言的方式訪問和修改一個文檔的內容和結構。換句話說,這是表示和處理一個HTML或XML文檔的常用方法。
有一點很重要,DOM的設計是以對象管理組織(OMG)的規約為基礎的,因此可以用于任何編程語言。最初人們把它認為是一種讓JavaScript在瀏覽器間可移植的方法,不過DOM的應用已經遠遠超出這個范圍。
DOM實際上是以面向對象方式描述的對象模型。DOM定義了表示和修改文檔所需的對象、這些對象的行為和屬性以及這些對象之間的關系。可以把DOM認為是頁面上數據和結構的一個樹形表示,不過頁面當然可能并不是以這種樹的方式具體實現。假設有一個Web頁面,如代碼清單2-5所示。
代碼清單2-5 簡單的表格
<table>
<tbody>
<tr>
<td>Foo</td>
<td>Bar</td>
</tr>
</tbody>
</table>
可以畫出這個簡單表格的DOM,如圖2-6所示。
DOM規約好就好在它提供了一種與文檔交互的標準方法。如果沒有DOM,Ajax最有意思的方面也許根本就沒有存在的可能。由于DOM不僅允許遍歷DOM樹,還可以編輯內容,因此可以建立極為動態的頁面。
2.9 小結
盡管Ajax風格的技術已經用了很多年,但直到最近XMLHttpRequest對象才得到現代瀏覽器的采納,而這也為開發豐富的Web應用開啟了一個新的時代。在本章中,我們討論了Ajax核心(即XMLHttpRequest對象)的相關基礎知識。我們了解了XMLHttpRequest對象的方法和屬性,而且展示了使用XMLHttpRequest對象的簡單示例。可以看到,這個對象相當簡單,無需你考慮其中很多的復雜性。適當地使用JavaScript,再加上基本的DOM管理,Ajax可以提供高度的交互性,而這在此前的Web上是做不到的。
第1章曾提到,利用XMLHttpRequest,你不必將整個頁面完全刷新,也不限于只能與服務器進行同步會話。在后面的幾章中,我們會介紹如何將你已經掌握的服務器端技術與XMLHttpRequest的獨特功能相結合,來提供高度交互性的Web應用。