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>