Ajax 這個名詞是由 Jesse James Garrett 提出,在他發表的〈Ajax: A New Approach to Web Applications〉 中談到 Google Suggest 與 Google Maps 使用到的技術,是他們在 Adaptive Path 中稱之為 Ajax 的新方法:
Google Suggest and Google Maps are two examples of a new approach to web applications that we at Adaptive Path have been calling Ajax. The name is shorthand for Asynchronous JavaScript + XML, and it represents a fundamental shift in what’s possible on the Web.
文中表示,Ajax 是非同步 JavaScript 結合 XML 的概念,XML 是用來交換結構化資料,但事實上,並非唯一可用的格式。
Ajax 的核心概念為非同步,為何要非同步?傳統表單提交、超鏈結點選,瀏覽器會有預設的處理方式,也就是以同步方式傳送請求,接著等待伺服器回應資料,然後進行換頁動作,在資料提交期間,使用者只能等待最新的畫面回應,中間若作了其它的頁面操作,瀏覽器可能會放棄原有的請求,就算在資料回應之後,使用者 面對的是全新的一個頁面,即使使用者真正所作的只是會更新畫面中某個區域。
如果可以把請求與回應改為非同步進行,也就是發出請求後,瀏覽器無需苦等伺服器的回應,而可以讓使用者對瀏覽 器中的 Web 應用程式進行其它的操作,又不會中斷原本的請求,當伺服器終於處理完請求並送出回應,而瀏覽器接收到回應時,再回過頭來呼叫瀏覽器所設定的對應 動作進行處理,方式是可以利用 DOM 操作更新畫面中的某些區域,那麼就開啟了各種可能的互動模式。
目前來說,在標準瀏覽器中使用 XMLHttpRequest 來建立非同步物件,可以如下呼叫建立非同步物件:
let request = new XMLHttpRequest();
基本上 XMLHttpRequest 可用的幾個方法如下:
void open(string method, string url[, boolean asynch, string username, string password])
開啟對伺服端的連結;method為 HTTP 請求方式('GET'、'POST'、'HEAD'等);url為伺服端位址,如果是GET的話,可加上請求參數與值;asynch為非同步設定,預設是true,表示使用非同步方式,username、password視伺服端有無要求驗證而設置。void setRequestHeader(string header, string value)為 HTTP 請求設定一個標頭值,在呼叫
open之後呼叫,通常在open的method參數為'POST'時使用。void send(content)對伺服端傳送請求,
open的method為'GET'時,content設為null,'POST'時content可放字串、XML、JSON 格式的內容,會放在POST本體中發送。在早期,瀏覽器不一定支援全部的 HTTP 方法。void abort()用來中斷請求。
string getAllResponseHeaders()傳回一個字串,其中包含 HTTP 請求的所有回應標頭。
string getResponseHeader(string header)傳回一個字串,其中包含指定的回應標頭值。
open 方法的第三個參數通常保留預設置 true,若想以同步方式,可以設為 false。若想知道目前請求物件狀態,可以在呼叫 open 方法之前,對 onreadystatechange 設置處理器函式。只要有狀態變化,則會呼叫所設置的處理器函式。
通常會在 onreadystatechange 的處理器函式中,偵測 XMLHttpRequest 物件的狀態,狀態可藉由 readyState 特性取得,這特性會有 0 到 4 的變化,代表各個處理階段,常數可透過 XMLHttpRequest 上的常數名稱取得::
XMLHttpRequest.UNSENT常數值 0,
XMLHttpRequest物件已建構。XMLHttpRequest.OPENED常數值 1,已成功呼叫
open方法,在這個狀態下,可以使用setRequestHeader,而後呼叫send方法。XMLHttpRequest.HEADERS_RECEIVED常數值 2,在呼叫過
send方法且接收到回應標頭的狀態。XMLHttpRequest.LOADING常數值 3,正在接收回應本體。
XMLHttpRequest.DONE常數值 4,伺服端回應結束,可能是資料傳輸完成,或者是傳送過程因發生錯誤而中斷(例如偵測到無限重導)。
通常只會對 readyState 為 XMLHttpRequest.DONE 時作處理,XMLHttpRequest 物件的 status 表示 HTTP 回應狀態碼,一個例子如下:
let request = new XMLHttpRequest();
request.onreadystatechange = function(evt) {
let req = evt.target;
if(req.readyState === XMLHttpRequest.DONE && req.status === 200) {
// 對成功回應作處理
}
};
request.open('GET', 'data.txt');
request.send(null);
(偶而地,也會針對 XMLHttpRequest.LOADING 來進行處理,例如接收到一個持續回應的串流(stream),像是模擬伺服端推播的時候。)
onreadystatechange 的第一個參數會是 Event 實例,其 target 特性會是 XMLHttpRequest 實例,可以使用 statusText 取得回應狀態碼代表的文字訊息,而 XMLHttpRequest 的 responseText 取得伺服端的回應文字,不過要注意,伺服端回應時若沒有指明字元集(例如 Content-Type: text/html; charset=UTF-8 之類),responseText 預設會使用 UTF-8 字元集來解讀傳回的文字。
如果回應是 XML,可以使用 responseXML 取得剖析後的 XML DOM 物件。
以下是一個非同步取得資料的完整流程示範,其中請求的純文件中包括中文,所以先儲存為 UTF-8 格式:
<table style="text-align: left; width: 100%;" border="1" cellpadding="2" cellspacing="2">
<tbody>
<tr>
<td style="vertical-align: top;">標題一</td>
<td style="vertical-align: top;">標題二</td>
<td style="vertical-align: top;">標題三</td>
<td style="vertical-align: top;">標題四</td>
</tr>
<tr>
<td style="vertical-align: top;">資料1</td>
<td style="vertical-align: top;">資料3</td>
<td style="vertical-align: top;">資料5</td>
<td style="vertical-align: top;">資料7</td>
</tr>
<tr>
<td style="vertical-align: top;">資料2</td>
<td style="vertical-align: top;">資料4</td>
<td style="vertical-align: top;">資料6</td>
<td style="vertical-align: top;">資料8</td>
</tr>
</tbody>
</table>
執行的結果是以非同步方式取得文件,並在同一個頁面顯示內容:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
</head>
<body>
<button id='req'>取得表格</button>
<div id="table"></div>
<script type="text/javascript">
document.getElementById('req').onclick = function() {
let request = new XMLHttpRequest();
request.onreadystatechange = function(evt) {
let req = evt.target;
if(req.readyState === XMLHttpRequest.DONE && req.status === 200) {
document.getElementById('table').innerHTML = req.responseText;
}
};
request.open('GET', 'XMLHttpRequest-1.txt');
request.send(null);
};
</script>
</body>
</html>

