Ajax從入門到搞定,一篇文章就夠了

英文原文:https://developer.mozilla.org/en-US/docs/AJAX/Getting_Started
翻譯者:不想成熟的大叔
什么是 Ajax?
Ajax 的意思就是異步的 JavaScript 和 XML。簡而言之,它是使用 XMLHttpRequest 對象與服務(wù)器端通信的腳本語言。它可以發(fā)送及接收各種格式的信息,包括 JSON、XML、HTML 和文本文件。
Ajax 最為吸引人的就是它的“異步”特性,這意味著 Ajax 可以無需刷新頁面而與服務(wù)器端進(jìn)行通信。允許你根據(jù)用戶事件來更新部分頁面內(nèi)容。
可以考慮的兩個特性:
向服務(wù)器端發(fā)送請求,而不用重新加載頁面。
從服務(wù)器端接收數(shù)據(jù)并處理。
第一步:如何發(fā)送一個 HTTP 請求
需要通過 XMLHttpRequest 實(shí)現(xiàn)使用 JavaScript 向服務(wù)器端發(fā)送一個 HTTP 請求。而 Internet Explorer(IE)中引入一個名為 XMLHTTP 的ActiveX對象實(shí)現(xiàn)與 XMLHttpRequest 相同的功能,Mozilla、Safari 和其他瀏覽器則使用 XMLHttpRequest。
如果要兼容各個瀏覽器的話,可以這樣來做:
var httpRequest;if (window.XMLHttpRequest) {// Mozilla, Safari, IE7+ ...httpRequest = new XMLHttpRequest();} else if (window.ActiveXObject) {// IE 6 and olderhttpRequest = new ActiveXObject("Microsoft.XMLHTTP");}
注意:出于演示目的,上面創(chuàng)建 XMLHTTP 實(shí)例是簡化了的代碼。關(guān)于更加真實(shí)的例子,請參閱本文的第三步。
接下來,當(dāng)接收到服務(wù)器端響應(yīng)時,需要告訴 HTTP 請求對象使用 JavaScript 函數(shù)來處理響應(yīng)。將 XMLHttpRequest 對象的 onreadystatechange 屬性設(shè)置為該函數(shù)的名稱,當(dāng)請求的狀態(tài)變化時,該函數(shù)會被調(diào)用。
httpRequest.onreadystatechange = nameOfTheFunction;
注意:該函數(shù)名沒有傳遞參數(shù)的括號和參數(shù),這表示只是分配了一個函數(shù)的引用,而不是真正調(diào)用該函數(shù)。當(dāng)然,也可以動態(tài)定義一個匿名函數(shù),這樣可以實(shí)時地處理響應(yīng)。
httpRequest.onreadystatechange = function () {// process the server response};
在處理完服務(wù)器端的響應(yīng)之后,我們就可以調(diào)用 XMLHttpRequest 對象的 open()?和 send()?方法向服務(wù)器端發(fā)送請求了。
httpRequest.open("GET", "http://www.example.org/some.file", true);httpRequest.send(null);
open()?方法的第一個參數(shù):HTTP 請求方法 – GET、POST、HEAD 及任何服務(wù)器端支持的方法。根據(jù) HTTP 標(biāo)準(zhǔn)保持大寫,否則一些瀏覽器(例如火狐)可能無法處理請求。關(guān)于 HTTP 請求方法的更多信息,你可以查看W3C 規(guī)范
open()?方法的第二個參數(shù):請求的 URL。出于安全考慮,不能調(diào)用第三方域的頁面內(nèi)容。當(dāng)調(diào)用 open()?方法時,一定確認(rèn)使用相同域名內(nèi)的頁面,否則會得到“permission denied”的錯誤提示。常見的錯誤是使用 domain.tld 訪問網(wǎng)站,卻使用 www.domain.tld 來請求頁面。如果真的需要發(fā)送一個請求到另一個域的話,可以查看 HTTP 訪問控制
open()?方法的第三個參數(shù):可選,是否是異步請求。如果是 true(默認(rèn)值),表示是異步請求。
send()?方法的參數(shù)表示當(dāng)請求為 POST 時,向服務(wù)器端發(fā)送請求的數(shù)據(jù)內(nèi)容。如果發(fā)送的是表單數(shù)據(jù)格式的話,服務(wù)器端可以向字符串一樣地解析。
"name=value&anothername=" + encodeURIComponent(myVar) + "&so=on";
向服務(wù)器端發(fā)送的數(shù)據(jù)格式也可以是 JSON、SOAP 等格式。
注意:如果使用 POST 方式發(fā)送數(shù)據(jù)的話,在調(diào)用 send()?方法前,需要設(shè)置請求的 MIME 類型。
httpRequest.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
第二步:處理服務(wù)器端的響應(yīng)
當(dāng)發(fā)送請求時,已經(jīng)定義了一個函數(shù)來處理響應(yīng)。
httpRequest.onreadystatechange = nameOfTheFunction;
這個函數(shù)可以做什么呢?首先,該函數(shù)需要檢查請求的狀態(tài)。如果狀態(tài)值為 4 的話,這表示接收到完成的服務(wù)器端響應(yīng),可以繼續(xù)處理。
if (httpRequest.readyState === 4) {// everything is good, the response is received} else {// still not ready}
readyState 的值列表如下:
0 – 未初始化
1 – 正在加載
2 – 加載完畢
3 – 交互中
4 – 完成
接下來需要檢查 HTTP 服務(wù)器端響應(yīng)的狀態(tài)代碼,W3C 網(wǎng)站?列出了所有的狀態(tài)代碼。下面的例子中,通過是否為 200 OK 的狀態(tài)碼來判斷 Ajax 調(diào)用是否是成功的。
if (httpRequest.status === 200) {// perfect!} else {// there was a problem with the request,// for example the response may contain a 404 (Not Found)// or 500 (Internal Server Error) response code}
在檢查了請求的狀態(tài)和響應(yīng)的狀態(tài)碼后,就可以接收服務(wù)器端發(fā)送的數(shù)據(jù)并處理。有兩種選項(xiàng)訪問這些數(shù)據(jù):
httpRequest.responseText:將服務(wù)器端響應(yīng)作為文本字符串返回
httpRequest.responseXML:將響應(yīng)作為一個 XMLDocument 對象返回,該對象可以使用 JavaScript DOM 函數(shù)進(jìn)行遍歷。
注意,上述步驟只有異步請求(open()?方法的第三個參數(shù)設(shè)置為 true)時才是有效的。如果使用同步請求的話,是不需要指定函數(shù)的。在調(diào)用 send()?方法后就可以訪問到服務(wù)器端返回的數(shù)據(jù),因?yàn)槟_本會停止并等待服務(wù)器端的響應(yīng)。
第三步:一個簡單的例子
下面來做一個簡單的 HTTP 請求。JavaScript 將請求一個包含“I’m a test.”文本的“test.html”HTML 文檔,然后使用alert()?方法打印 test.html 文件的內(nèi)容。
<span id="ajaxButton" style="cursor: pointer; text-decoration: underline">Make a requestspan><script type="text/javascript">(function () {var httpRequest;document.getElementById("ajaxButton").onclick = function () {makeRequest("test.html");};function makeRequest(url) {if (window.XMLHttpRequest) {// Mozilla, Safari, ...httpRequest = new XMLHttpRequest();} else if (window.ActiveXObject) {// IEtry {httpRequest = new ActiveXObject("Msxml2.XMLHTTP");} catch (e) {try {httpRequest = new ActiveXObject("Microsoft.XMLHTTP");} catch (e) {}}}if (!httpRequest) {alert("Giving up :( Cannot create an XMLHTTP instance");return false;}httpRequest.onreadystatechange = alertContents;httpRequest.open("GET", url);httpRequest.send();}function alertContents() {if (httpRequest.readyState === 4) {if (httpRequest.status === 200) {alert(httpRequest.responseText);} else {alert("There was a problem with the request.");}}}})();script>
在這個例子中:
在瀏覽器中用戶單擊“Make a request”鏈接;
事件處理器調(diào)用 makeRequest()?方法,通過向該函數(shù)傳遞的參數(shù),請求一個處在同一目錄中的“test.html”HTML 文件;
請求后,(onreadystatechange)執(zhí)行 alertContents()?方法;
alertContents()?方法用于檢查如果正確地接收到響應(yīng),利用 alert()?方法打印“test.html”文件包含的內(nèi)容。
注意:
如果你發(fā)送一個請求后返回的是一段 XML 代碼,而不是一個靜態(tài)的 XML 文件的話,在 Internet Explorer 中必須設(shè)置一些響應(yīng)頭。如果沒有設(shè)置響應(yīng)頭“Content-Type: application/xml”的話,當(dāng)試圖訪問 XML 元素時 IE 將拋出一個 "Object Expected" 的 JavaScript 錯誤。
如果沒有設(shè)置頭“Cache-Control: no-cache”的話,瀏覽器將緩存響應(yīng)并不會重新提交請求??梢蕴砑酉駮r間戳或一個隨機(jī)數(shù)的不同 GET 請求參數(shù)(參考 bypassing the cache)。
如果 httpRequest 變量是全局的,makeRequest()?方法因?yàn)闆_突可能會被重寫。將 httpRequest 變量定義在一個閉包中的話,可以避免 Ajax 函數(shù)的沖突。
如果出現(xiàn)通信錯誤(如服務(wù)器端被關(guān)閉),當(dāng)試圖訪問狀態(tài)字段時在 onreadystatechange 的方法中將會拋出一個異常。確保 if 語句聲明在 try..catch 語句中。
function alertContents() {try {if (httpRequest.readyState === 4) {if (httpRequest.status === 200) {alert(httpRequest.responseText);} else {alert("There was a problem with the request.");}}} catch (e) {alert("Caught Exception: " + e.description);}}
第四步:使用 XML 進(jìn)行響應(yīng)
在前面的例子中,當(dāng)接收到響應(yīng)后使用 XMLHttpRequest 對象的 responseText 屬性訪問“test.html”文件包含的內(nèi)容?,F(xiàn)在嘗試一下 responseXML 屬性。
首先,創(chuàng)建一個用于請求的名為“test.xml”的有效 XML 文檔,代碼如下:
<root>I'm a test.root>
在腳本中,只需要修改請求行:
onclick="makeRequest('test.xml')"
然后在 alertContents()?方法中,需要使用如下代碼替換 alert(httpRequest.responseText);?這一行代碼:
var xmldoc = httpRequest.responseXML;var root_node = xmldoc.getElementsByTagName("root").item(0);alert(root_node.firstChild.data);
這段代碼需要由 responseXML 給予的 XMLDocument 對象,然后使用 DOM 方法來訪問 XML 文檔中的數(shù)據(jù)。
第五步:處理數(shù)據(jù)
最后,向服務(wù)器端發(fā)送一些數(shù)據(jù)并接收響應(yīng)。這次 JavaScript 腳本請求一個動態(tài)頁面“test.php”,該頁面將根據(jù)發(fā)送的數(shù)據(jù)返回一個“computedString”-“Hello, [user data]!”,并使用 alert()?方法進(jìn)行打印。
首先,向 HTML 頁面中添加一個文本框,用戶可以通過該文本框輸入他們的名字:
<label>Your name:<input type="text" id="ajaxTextbox" />label><span id="ajaxButton" style="cursor: pointer; text-decoration: underline">Make a requestspan>
還需要添加一行事件處理器用于從文本框獲取用戶的數(shù)據(jù),并將該數(shù)據(jù)隨著 URL 傳遞給 makeRequest()?方法:
document.getElementById("ajaxButton").onclick = function () {var userName = document.getElementById("ajaxTextbox").value;makeRequest("test.php", userName);};
修改 makeRequest()?方法用于接收用戶數(shù)據(jù)并發(fā)送給服務(wù)器端。將請求方式從 GET 修改為 POST,用戶數(shù)據(jù)作為參數(shù)傳遞給 httpRequest.send()?方法:
function makeRequest(url, userName) {...httpRequest.onreadystatechange = alertContents;httpRequest.open('POST', url);httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');httpRequest.send('userName=' + encodeURIComponent(userName));}
alertContents()?方法可以向第三步一樣利用 alert()?方法打印服務(wù)器端返回的數(shù)據(jù)。假設(shè)服務(wù)器端返回的是 computedString 和用戶數(shù)據(jù)的話,如果用戶在文本框中輸入“Jane”服務(wù)器端響應(yīng)的內(nèi)容會像是這樣:
{"userData":"Jane","computedString":"Hi, Jane!"}
在 alertContents()?方法中使用這些數(shù)據(jù),不僅可以使用 alert()?方法打印 responseText 的內(nèi)容,還可以將其解析并打印 computedString 屬性內(nèi)容:
function alertContents() {if (httpRequest.readyState === 4) {if (httpRequest.status === 200) {var response = JSON.parse(httpRequest.responseText);alert(response.computedString);} else {alert("There was a problem with the request.");}}}本文完?

