天天看點

AJAX核心對象-- XMLHttpRequest 對象使用詳解 (一)

  多數 Web 應用程式都使用請求/響應模型從伺服器上獲得完整的HTML 頁面。常常是點選一個按鈕,等待伺服器響應,再點選另一個按鈕,然後再等待,這樣一個反複的過程。有了 Ajax 和 XMLHttpRequest 對象,就可以使用不必讓使用者等待伺服器響應的請求/響應模型了。 本文中,Brett McLaughlin 介紹了如何建立能夠适應不同浏覽器的 XMLHttpRequest 執行個體,建立和發送請求,并響應伺服器。

   本文中,您将開始接觸最基本和基礎性的有關 Ajax 的全部對象和程式設計方法:XMLHttpRequest 對象。該對象實際上僅僅是一個跨越所有 Ajax 應用程式的公共線程,您可能已經預料到,隻有徹底了解該對象才能充分發揮程式設計的潛力。事實上,有時您會發現,要正确地使用 XMLHttpRequest,顯然不能 使用 XMLHttpRequest。這到底是怎麼回事呢?

  Web 2.0 一瞥

   在深入研究代碼之前首先看看最近的觀點 —— 一定要十厘清楚 Web 2.0 這個概念。聽到 Web 2.0 這個詞的時候,應該首先問一問 “Web 1.0 是什麼?” 雖然很少聽人提到 Web 1.0,實際上它指的就是具有完全不同的請求和響應模型的傳統 Web。比如,到 Amazon.com 網站上點選一個按鈕或者輸入搜尋項。就會對伺服器發送一個請求,然後響應再傳回到浏覽器。該請求不僅僅是圖書和書目清單,而是另一個完整的 HTML 頁面。是以當 Web 浏覽器用新的 HTML 頁面重繪時,可能會看到閃爍或抖動。事實上,通過看到的每個新頁面可以清晰地看到請求和響應。

   Web 2.0(在很大程度上)消除了這種看得見的往複互動。比如通路 Google Maps 或 Flickr 這樣的站點(到這些支援 Web 2.0 和 Ajax 站點的連結請參閱 參考資料)。比如在 Google Maps 上,您可以拖動地圖,放大和縮小,隻有很少的重繪操作。當然這裡仍然有請求和響應,隻不過都藏到了幕後。作為使用者,體驗更加舒适,感覺很像桌面應用程式。 這種新的感受和範型就是當有人提到 Web 2.0 時您所體會到的。

  需要關心的是如何使這些新的互動成為可能。顯然,仍然需要發出 請求和接收響應,但正是針對每次請求/響應互動的 HTML 重繪造成了緩慢、笨拙的 Web 互動的感受。是以很清楚,我們需要一種方法使發送的請求和接收的響應隻 包含需要的資料而不是整個 HTML 頁面。惟一需要獲得整個新 HTML 頁面的時候就是希望使用者看到 新頁面的時候。

  但多數互動都是在已有頁面上增加細節、修改主體文本或者覆寫原有資料。這些情況 下,Ajax 和 Web 2.0 方法允許在不 更新整個 HTML 頁面的情況下發送和接收資料。對于那些經常上網的人,這種能力可以讓您的應用程式感覺更快、響應更及時,讓他們不時地光顧您的網站。

  XMLHttpRequest 簡介

   要真正實作這種絢麗的奇迹,必須非常熟悉一個JavaScript 對象,即 XMLHttpRequest。這個小小的對象實際上已經在幾種浏覽器中存在一段時間了,它是本專欄今後幾個月中要介紹的 Web 2.0、Ajax 和大部分其他内容的核心。為了讓您快速地大體了解它,下面給出将要用于該對象的很少的幾個 方法和屬性。

  ·open():建立到伺服器的新請求。

  ·send():向伺服器發送請求。

  ·abort():退出目前請求。

  ·readyState:提供目前 HTML 的就緒狀态。

  ·responseText:伺服器傳回的請求響應文本。

   如果不了解這些(或者其中的任何 一個),您也不用擔心,後面幾篇文章中我們将介紹每個方法和屬性。現在應該 了解的是,明确用 XMLHttpRequest 做什麼。要注意這些方法和屬性都與發送請求及處理響應有關。事實上,如果看到 XMLHttpRequest 的所有方法和屬性,就會發現它們都 與非常簡單的請求/響應模型有關。顯然,我們不會遇到特别新的 GUI 對象或者建立使用者互動的某種超極神秘的方法,我們将使用非常簡單的請求和非常簡單的響應。聽起來似乎沒有多少吸引力,但是用好該對象可以徹底改變您的應用 程式。

  簡單的 new

  首先需要建立一個新變量并賦給它一個 XMLHttpRequest 對象執行個體。這在 JavaScript 中很簡單,隻要對該對象名使用 new 關鍵字即可,如 清單 1 所示。

  清單 1. 建立新的 XMLHttpRequest 對象

<script language="javascript" type="text/javascript">

 var request = new XMLHttpRequest();

</script>

  不難吧?記住,JavaScript 不要求指定變量類型,是以不需要像 清單 2 那樣做(在 Java 語言中可能需要這樣)。

   清單 2. 建立 XMLHttpRequest 的 Java 僞代碼

XMLHttpRequest request = new XMLHttpRequest();

  是以在 JavaScript 中用 var 建立一個變量,給它一個名字(如 “request”),然後賦給它一個新的 XMLHttpRequest 執行個體。此後就可以在函數中使用該對象了。

  錯誤處理

   在實際上各種事情都可能出錯,而上面的代碼沒有提供任何錯誤處理。較好的辦法是建立該對象,并在出現問題時優雅地退出。比如,任何較早的浏覽器(不論您 是否相信,仍然有人在使用老版本的Netscape Navigator)都不支援 XMLHttpRequest,您需要讓這些使用者知道有些地方出了問題。清單 3 說明如何建立該對象,以便在出現問題的時候發出 JavaScript 警告。

  清單 3. 建立具有錯誤處理能力的 XMLHttpRequest

<script language="javascript" type="text/javascript">

 var request = false;

 try {

  request = new XMLHttpRequest();

 } catch (failed) {

  request = false;

 }

 if (!request)

  alert("Error initializing XMLHttpRequest!");

</script>

  一定要了解這些步驟:

  建立一個新變量 request 并指派 false。後面将使用 false 作為判定條件,它表示還沒有建立 XMLHttpRequest 對象。

  ·增加 try/catch 塊:

  ·嘗試建立 XMLHttpRequest 對象。

    1、如果失敗(catch (failed))則保證 request 的值仍然為 false。

    2、檢查 request 是否仍為 false(如果一切正常就不會是 false)。

  ·如果出現問題(request 是 false)則使用 JavaScript 警告通知使用者出現了問題。

  代碼非常簡單,對大多數 JavaScript 和 Web 開發人員來說,真正了解它要比讀寫代碼花更長的時間。現在已經得到了一段帶有錯誤檢查的 XMLHttpRequest 對象建立代碼,還可以告訴您哪兒出了問題。

應付 Microsoft

  看起來似乎一切良好,至少在用 Internet Explorer 試驗這些代碼之前是這樣的。如果這樣試驗的話,就會看到 圖 1 所示的糟糕情形。

AJAX核心對象-- XMLHttpRequest 對象使用詳解 (一)
圖 1. Internet Explorer 報告錯誤

  顯然有什麼地方不對勁,而 Internet Explorer 很難說是一種過時的浏覽器,因為全世界有 70% 在使用 Internet Explorer。換句話說,如果不支援 Microsoft 和 Internet Explorer 就不會受到 Web 世界的歡迎!是以我們需要采用不同的方法處理 Microsoft 浏覽器。

  經驗證發現 Microsoft 支援 Ajax,但是其 XMLHttpRequest 版本有不同的稱呼。事實上,它将其稱為幾種 不同的東西。如果使用較新版本的 Internet Explorer,則需要使用對象 Msxml2.XMLHTTP,而較老版本的 Internet Explorer 則使用 Microsoft.XMLHTTP。我們需要支援這兩種對象類型(同時還要支援非 Microsoft 浏覽器)。請看看 清單 4,它在前述代碼的基礎上增加了對 Microsoft 的支援。

  Microsoft 參與了嗎?

  關于 Ajax 和 Microsoft 對該領域不斷增長的興趣和參與已經有很多文章進行了介紹。事實上,據說 Microsoft 最新版本的 Internet Explorer —— version 7.0,将在 2006 年下半年推出 —— 将開始直接支援 XMLHttpRequest,讓您使用 new 關鍵字代替所有的 Msxml2.XMLHTTP 建立代碼。但不要太激動,仍然需要支援舊的浏覽器,是以跨浏覽器代碼不會很快消失。

  清單 4. 增加對 Microsoft 浏覽器的支援

<script language="javascript" type="text/javascript">

 var request = false;

 try {

  request = new XMLHttpRequest();

 } catch (trymicrosoft) {

  try {

   request = new ActiveXObject("Msxml2.XMLHTTP");

  } catch (othermicrosoft) {

   try {

    request = new ActiveXObject("Microsoft.XMLHTTP");

   } catch (failed) {

    request = false;

   }

  }

 }

 if (!request)

  alert("Error initializing XMLHttpRequest!");

</script>

  很容易被這些花括号迷住了眼睛,是以下面分别介紹每一步:

  ·建立一個新變量 request 并指派 false。使用 false 作為判斷條件,它表示還沒有建立 XMLHttpRequest 對象。

  ·增加 try/catch 塊:

   1、嘗試建立 XMLHttpRequest 對象。

   2、如果失敗(catch (trymicrosoft)):

    1) 嘗試使用較新版本的 Microsoft 浏覽器建立 Microsoft 相容的對象(Msxml2.XMLHTTP)。

    2) 如果失敗(catch (othermicrosoft))嘗試使用較老版本的 Microsoft 浏覽器建立 Microsoft 相容的對象(Microsoft.XMLHTTP)。

    3) 如果失敗(catch (failed))則保證 request 的值仍然為 false。

  ·檢查 request 是否仍然為 false(如果一切順利就不會是 false)。

  ·如果出現問題(request 是 false)則使用 JavaScript 警告通知使用者出現了問題。

  這樣修改代碼之後再使用 Internet Explorer 試驗,就應該看到已經建立的表單(沒有錯誤消息)。我實驗的結果如 圖 2 所示。

AJAX核心對象-- XMLHttpRequest 對象使用詳解 (一)
圖 2. Internet Explorer 正常工作

  靜态與動态

  再看一看清單 1、3 和 4,注意,所有這些代碼都直接嵌套在 script 标記中。像這種不放到方法或函數體中的 JavaScript 代碼稱為靜态 JavaScript。就是說代碼是在頁面顯示給使用者之前的某個時候運作。(雖然根據規範不能完全精确地 知道這些代碼何時運作對浏覽器有什麼影響,但是可以保證這些代碼在使用者能夠與頁面互動之前運作。)這也是多數 Ajax 程式員建立 XMLHttpRequest 對象的一般方式。

  就是說,也可以像 清單 5 那樣将這些代碼放在一個方法中。

   清單 5. 将 XMLHttpRequest 建立代碼移動到方法中

<script language="javascript" type="text/javascript">

var request;

function createRequest() {

 try {

  request = new XMLHttpRequest();

 } catch (trymicrosoft) {

  try {

   request = new ActiveXObject("Msxml2.XMLHTTP");

  } catch (othermicrosoft) {

   try {

    request = new ActiveXObject("Microsoft.XMLHTTP");

   } catch (failed) {

    request = false;

   }

  }

 }

 if (!request)

  alert("Error initializing XMLHttpRequest!");

}

</script>

  如果按照這種方式編寫代碼,那麼在處理 Ajax 之前需要調用該方法。是以還需要 清單 6 這樣的代碼。

  清單 6. 使用 XMLHttpRequest 的建立方法

<script language="javascript" type="text/javascript">

var request;

function createRequest() {

 try {

  request = new XMLHttpRequest();

 } catch (trymicrosoft) {

  try {

   request = new ActiveXObject("Msxml2.XMLHTTP");

  } catch (othermicrosoft) {

   try {

    request = new ActiveXObject("Microsoft.XMLHTTP");

   } catch (failed) {

    request = false;

   }

  }

 }

 if (!request)

  alert("Error initializing XMLHttpRequest!");

}

function getCustomerInfo() {

 createRequest();

 // Do something with the request variable

}

</script>

  此代碼惟一的問題是推遲了錯誤通知,這也是多數 Ajax 程式員不采用這一方法的原因。假設一個複雜的表單有 10 或 15 個字段、選擇框等,當使用者在第 14 個字段(按照表單順序從上到下)輸入文本時要激活某些 Ajax 代碼。這時候運作 getCustomerInfo() 嘗試建立一個 XMLHttpRequest 對象,但(對于本例來說)失敗了。然後向使用者顯示一條警告,明确地告訴他們不能使用該應用程式。但使用者已經花費了很多時間在表單中輸入資料!這是非常令人 讨厭的,而讨厭顯然不會吸引使用者再次通路您的網站。 如果使用靜态 JavaScript,使用者在點選頁面的時候很快就會看到錯誤資訊。這樣也很煩人,是不是?可能令使用者錯誤地認為您的 Web 應用程式不能在他的浏覽器上運作。不過,當然要比他們花費了 10 分鐘輸入資訊之後再顯示同樣的錯誤要好。是以,我建議編寫靜态的代碼,讓使用者盡可能早地發現問題。

用 XMLHttpRequest 發送請求

  得到請求對象之後就可以進入請求/響應循環了。記住,XMLHttpRequest 惟一的目的是讓您發送請求和接收響應。 其他一切都是 JavaScript 、CSS 或頁面中其他代碼的工作:改變使用者界面、切換圖像、解釋伺服器傳回的資料。準備好 XMLHttpRequest 之後,就可以向伺服器發送請求了。

  歡迎使用沙箱

  Ajax 采用一種沙箱安全模型。是以,Ajax 代碼(具體來說就是 XMLHttpRequest 對象)隻能對所在的同一個域發送請求。 以 後的文章中将進一步介紹安全和 Ajax,現在隻要知道在本地機器上運作的代碼隻能對本地機器上的伺服器端腳本發送請求。如果讓 Ajax 代碼在 www.breakneckpizza.com 上運作,則必須 www.breakneck.com 中運作的腳本發送請求。

  設定伺服器 URL

   首先要确定連接配接的伺服器的 URL。這并不是 Ajax 的特殊要求,但仍然是建立連接配接所必需的,顯然現在您應該知道如何構造 URL 了。多數應用程式中都會結合一些靜态資料和使用者處理的表單中的資料來構造該 URL。比如,清單 7 中的 JavaScript 代碼擷取電話号碼字段的值并用其構造 URL。

  清單 7.1 建立請求 URL

<script language="javascript" type="text/javascript">

var request = false;

try {

 request = new XMLHttpRequest();

} catch (trymicrosoft) {

 try {

  request = new ActiveXObject("Msxml2.XMLHTTP");

 } catch (othermicrosoft) {

  try {

   request = new ActiveXObject("Microsoft.XMLHTTP");

  } catch (failed) {

   request = false;

  }

 }

}

if (!request)

alert("Error initializing XMLHttpRequest!");

function getCustomerInfo() {

 var phone = document.getElementById("phone").value;

 var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone);

}

</script>

    清單 7.2 建立相容IE7.0的請求

<script language="javascript" type="text/javascript">

var request = false;

try {

 request = new ActiveXObject("Microsoft.XMLHTTP");

} catch (trymicrosoft) {

 try {

  request = new ActiveXObject("Msxml2.XMLHTTP");

 } catch (othermicrosoft) {

  try {

   request = new XMLHttpRequest();

  } catch (failed) {

   request = false;

  }

 }

}

if (!request)

alert("Error initializing XMLHttpRequest!");

function getCustomerInfo() {

 var phone = document.getElementById("phone").value;

 var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone);

}

</script>

  這裡沒有難懂的地方。首先,代碼建立了一個新變量 phone,并把 ID 為 “phone” 的表單字段的值賦給它。清單 8 展示了這個表單的 XHTML ,其中可以看到 phone 字段及其 id 屬性。

  清單 8. Break Neck Pizza 表單

<body>

 <p><img src="breakneck-logo_4c.gif" alt="Break Neck Pizza" /></p>

 <form action="POST">

  <p>Enter your phone number:

   <input type="text" size="14" name="phone" id="phone" onChange="getCustomerInfo();" />

  </p>

  <p>Your order will be delivered to:</p>

  <div id="address"></div>

  <p>Type your order in here:</p>

  <p><textarea name="order" rows="6" cols="50" id="order"></textarea></p>

  <p><input type="submit" value="Order Pizza" id="submit" /></p>

 </form>

</body>

  還要注意,當使用者輸入電話号碼或者改變電話号碼時,将觸發 清單 8 所示的 getCustomerInfo() 方法。該方法取得電話号碼并構造存儲在 url 變量中的 URL 字元串。記住,由于 Ajax 代碼是沙箱型的,因而隻能連接配接到同一個域,實際上 URL 中不需要域名。該例中的腳本名為 /cgi-local/lookupCustomer.php。最後,電話号碼作為 GET 參數附加到該腳本中:"phone=" + escape(phone)。

  如果以前沒用見過 escape() 方法,它用于轉義不能用明文正确發送的任何字元。比如,電話号碼中的空格将被轉換成字元 %20,進而能夠在 URL 中傳遞這些字元。

  可以根據需要添加任意多個參數。比如,如果需要增加另一個參數,隻需要将其附加到 URL 中并用 “與”(&)字元分開 [第一個參數用問号(?)和腳本名分開]。

   打開請求

  有了要連接配接的 URL 後就可以配置請求了。可以用 XMLHttpRequest 對象的 open() 方法來完成。該方法有五個參數:

  ·request-type:發送請求的類型。典型的值是 GET 或 POST,但也可以發送 HEAD 請求。

  ·url:要連接配接的 URL。

  ·asynch:如果希望使用異步連接配接則為 true,否則為 false。該參數是可選的,預設為 true。

  ·username:如果需要身份驗證,則可以在此指定使用者名。該可選參數沒有預設值。

  ·password:如果需要身份驗證,則可以在此指定密碼。該可選參數沒有預設值。

  通常使用其中的前三個參數。事實上,即使需要異步連接配接,也應該指定第三個參數為 “true”。這是預設值,但堅持明确指定請求是異步的還是同步的更容易了解。

  将這些結合起來,通常會得到 清單 9 所示的一行代碼。

  open() 是打開嗎?

   Internet 開發人員對 open() 方法到底做什麼沒有達成一緻。但它實際上并不是 打開一個請求。如果監控 XHTML/Ajax 頁面及其連接配接腳本之間的網絡和資料傳遞,當調用 open() 方法時将看不到任何通信。不清楚為何選用了這個名字,但顯然不是一個好的選擇。

  清單 9. 打開請求

function getCustomerInfo() {

 var phone = document.getElementById("phone").value;

var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone);

 request.open("GET", url, true);

}

  一旦設定好了 URL,其他就簡單了。多數請求使用 GET 就夠了(後面的文章中将看到需要使用 POST 的情況),再加上 URL,這就是使用 open() 方法需要的全部内容了。

繼續閱讀