JavaScript AJAX (Asynchronous JavaScript and XML)
AJAX 的全名是 Asynchronous JavaScript and XML。
一般你瀏覽一個新的網頁,你的瀏覽器 (browser) 要先等遠端伺服器 (web server) 傳回網頁資料後,才載入這一整個新的頁面,在這等待的期間你沒法做其他事情只能等,這種行為我們稱做同步 (synchronous)。
那 AJAX 是什麼樣子的技術,AJAX 的特性是什麼?
- 無須重新載入整個頁面,便能對遠端伺服器發送請求 (請求會在瀏覽器背景執行)。
- 讓你可以只更新網頁中的一小部分內容,也因為 server 返回的資料量更小,讓網頁反應速度更快。
- 在跟 server 請求新內容同時,你可以在頁面中繼續做其他操作,不用停住等待,這種行為我們稱做非同步 (asynchronous)。
AJAX 名稱中有一個 XML,本意是指從 server 端返回的資料格式,這讓初學者特別容易誤會的是,那 AJAX 是跟 XML 有直接關聯嗎?還是一定只能用 XML 格式?答案是沒有直接關係,反而現在實務上最常用的 AJAX 資料交換格式是 JSON。
AJAX 只是一種網頁程式設計概念,它是由幾個既有的技術所組成:
- 利用 JavaScript 的 XMLHttpRequest 物件 (object) 與遠端的 web server 進行非同步的資料交換。
- 利用 JavaScript / DOM / HTML / CSS 來動態改變頁面上的內容。
AJAX 的運作流程
AJAX 的運作概念大概是這樣子的:
圖片來自於 Wikipedia AJAX
- 當頁面上某個事件 (event) 發生時,例如使用者點了某個按鈕。
- 建立一個 JavaScript XMLHttpRequest object。
- 透過 XMLHttpRequest 物件來向遠端 web server 發送 HTTP 請求 (request)。
- web server 返回請求結果。
- 在 client 端用 JavaScript 讀取返回的結果 (如果判斷是 JSON 資料格式,則用 JSON.parse 解開內容)。
- 根據返回的資料,做任何後續動作 (或不動作),例如更新網頁上的內容。
XMLHttpRequest Object
XMLHttpRequest 物件用來建立 HTTP 請求,語法:
var httpRequest = new XMLHttpRequest();
IE 在 IE7 開始才支援 XMLHttpRequest Object,如果是 IE6 以下則要用 IE 專屬的 ActiveXObject:
var httpRequest = new ActiveXObject('Microsoft.XMLHTTP');
XMLHttpRequest 物件建立後,接下來就用其 onreadystatechange 方法來綁定等 server 資料返回時要執行的 callback 函數:
httpRequest.onreadystatechange = function() {
// ...
};
XMLHttpRequest.open(method, url, async)
接下來用 open 方法指定要請求的網址 (URL),語法:
httpRequest.open(method, url, async);
- 參數 method 指定 HTTP method 像是 "GET", "POST", "PUT", "DELETE"
- 參數 url 指定要發送請求的網址
- 參數 async 是一個布林值 (boolean) 表示是否要做非同步請求,預設是 true 非同步
例子:
httpRequest.open('GET', '/api/test.php?param=foobar');
XMLHttpRequest.send(null | formData)
最後用 send 方法發送出遠端 HTTP 請求:
httpRequest.send(null);
如果你是想用 HTTP POST,send 方法的第一個參數可以用來傳值,值的格式是 Form data 格式:
httpRequest.open('POST', '/api/test.php');
httpRequest.send('name=value&anothername=' + encodeURIComponent(myVar) + '&so=on');
XMLHttpRequest.setRequestHeader(header, value)
此外,當你使用 HTTP POST 資料的時候,需要設定 HTTP 的 Content-Type 標頭 (header) 為 application/x-www-form-urlencoded,你可以用 setRequestHeader 方法來設定 HTTP header:
httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
XMLHttpRequest.readyState, XMLHttpRequest.statusText, XMLHttpRequest.responseText
XMLHttpRequest 物件還有幾個屬性 (property) 你需要知道的:
屬性 | 說明 |
---|---|
readyState | 目前 XMLHttpRequest 物件的狀態 0 (uninitialized) 1 (loading) 2 (loaded) 3 (interactive) 4 (complete) 請求完成 |
status | 返回的 HTTP 狀態 (status)。像是: 200: "OK" 403: "Forbidden" 404: "Page not found" |
responseText | server 返回的資料,是一個字串 |
XMLHttpRequest.onreadystatechange
而每當 XMLHttpRequest 物件狀態改變時,透過 onreadystatechange 綁定的函數就會被執行,接著我們來看怎麼處理 server 返回的資料 (HTTP response):
httpRequest.onreadystatechange = function() {
// 等狀態變成請求完成狀態
if (httpRequest.readyState === 4) {
// 只處理 server 返回正常的 HTTP 200 狀態
if (httpRequest.status == 200) {
// 顯示 server 返回的內容
alert(httpRequest.responseText);
// 或,如果返回的資料格式是 JSON
var response = JSON.parse(httpRequest.responseText);
// ...
}
}
};
XMLHttpRequest.getResponseHeader(name)
另外,getResponseHeader 方法可以用來取得 HTTP response 的 header:
httpRequest.onreadystatechange = function() {
if (httpRequest.readyState === 4) {
if (httpRequest.status == 200) {
// 顯示 Last-Modified response header
alert(httpRequest.getResponseHeader('Last-Modified'));
// 顯示 Content-Type response header
httpRequest.getResponseHeader('Content-Type');
// ...
}
}
};
Async = false
前面提到 open 方法的第三個參數是 false 時,表示使用同步模式 (synchronous),一般很少使用,如果你指定同步模式,在發送 HTTP request 直到 server 返回資料這期間,JavaScript 會停止執行網頁也會暫停住不能操作。
而 Async = false 時,也不是用 onreadystatechange callback,而是在 send 後面直接取得 response data (因為執行 send 之後,JavaScript 會停止執行一直等到 reponse 回來為止):
httpRequest.open('GET', '/api/test', false);
httpRequest.send();
alert(httpRequest.responseText);
AJAX 完整範例 (Example)
// 建立 XMLHttpRequest 物件
var httpRequest;
if (window.XMLHttpRequest) {
httpRequest = new XMLHttpRequest();
} else {
// 跨瀏覽器相容 IE6 以下
httpRequest = new ActiveXObject('Microsoft.XMLHTTP');
}
// AJAX callback
httpRequest.onreadystatechange = function() {
// 等狀態變成請求完成狀態
if (httpRequest.readyState === 4) {
// 只處理 server 返回正常的 HTTP 200 狀態
if (httpRequest.status == 200) {
// 解開 server 返回的 JSON 資料格式
var jsonResponse = JSON.parse(httpRequest.responseText);
// 更新頁面內容
document.getElementById('user').innerHTML = jsonResponse.userName;
} else {
alert('ERROR - server status code: ' + httpRequest.status);
}
}
};
// 使用 HTTP GET 方法,從 URL /api/get_something 請求資料
httpRequest.open('GET', '/api/get_something');
// 送出 HTTP 請求
httpRequest.send(null);