最近由於項目需要,專心研究了一下Ajax的相關程式設計,本來一開始想用Prototype或者jQuery等架構,後來發現其實用不到這些架構裡面的那麼多內容,強行使用的話只能拖累我網站的訪問者,降低使用者體驗,因此決定自己寫一套適合自己需求的Ajax程式碼程式庫。 在這套Ajax程式碼程式庫中,實現了如下的功能:1、Ajax遠程調用資料2、通過Ajax非同步提交Form表單3、返回資料後,能夠將資料繫結到頁面的相關控制項內(如:div、select、ul、span等等)4、讓Ajax程式支援瀏覽器的前進、後退按鈕
一、什麼是Ajax,他都能做什嗎?
AJAX全稱為“Asynchronous JavaScript and XML”(非同步JavaScript和XML),是一種建立互動式網頁應用的網頁開發技術。使用Ajax的最大優點,就是能在不更新整個頁面的前提下維護資料。這使得Web應用程式更為迅捷地回應使用者動作,並避免了在網路上發送那些沒有改變過的資訊。Ajax不需要任何瀏覽器外掛程式,但需要使用者允許JavaScript在瀏覽器上執行。就像DHTML應用程式那樣,Ajax應用程式必須在眾多
不同的瀏覽器和平台上經過嚴格的測試。隨著Ajax的成熟,一些簡化Ajax使用方法的程式庫也相繼問世。同樣,也出現了另一種輔助程式設計的技術,為那
些不支援JavaScript的使用者提供替代功能。
二、核心代碼
所謂核心代碼就是提供一個能夠跨瀏覽器的Ajax調用代碼,這部分代碼是後面擴充代碼的必備部分,在這部分代碼中我們提供了一個建立XMLHttpRequest對象並能夠與Web伺服器進行非同步資料交換。 在我寫的這些代碼中,自訂了兩個相關的函數,一個是通用的擷取HTML對象函數,還有一個就是自動過濾字串開頭結尾的空格,代碼如下:function $( elementId ) {
return document.getElementById(elementId);
}
function trim(str){ //刪除左右兩端的空格
return str.replace(/(^/s*)|(/s*$)/g, "");
} Ajax的核心代碼如下: /*
* 根據不同的瀏覽器,擷取Ajax對象
*/
function getAjaxObject() {
var xmlHttpRequest;
// 判斷是否把XMLHttpRequest實現為一個本地javascript對象
if(window.XMLHttpRequest){
xmlHttpRequest = new XMLHttpRequest();
}else if(window.ActiveXObject){ // 判斷是否支援ActiveX控制項
try{
// 通過執行個體化ActiveXObject的一個新執行個體來建立XMLHttpRequest對象
xmlHttpRequest = new ActiveXObject("Microsoft.XMLHTTP"); // msxml3以上版本
}catch(e){
try{
// 通過執行個體化ActiveXObject的一個新執行個體來建立XMLHttpRequest對象
xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP"); // msxml3以下版本
}catch(e){}
}
}
if ( !xmlHttpRequest ) {
alert("建立Ajax對象失敗,您將無法正常瀏覽網頁");
}
return xmlHttpRequest;
}
/*
* 非同步方式提交請求
*/
function sendRequestByAjax(method, url, data, dataHandler) {
// 擷取Ajax對象
request = getAjaxObject();
// 設定回呼函數
if( !IE4 ){
request.onload = dataHandler;
} else {
request.onreadystatechange = dataHandler;
}
request.open(method, url, true); // true代表使用非同步方式 false代表使用同步方式
// 處理提交方式
if ( "get" == method.toLowerCase() ) {
// 使用GET方式提交資料
var urls = url.split("?");
if ( urls[1] == "" || typeof(urls[1]) == "undefined" ) {
url = urls[0] + "?" + data;
} else {
url = urls[0] + "?" + urls[1] + "&" + data;
}
data = null; // for GET method,request必須為空白
} else if ( "post" == method.toLowerCase() ){
// 使用POST方式提交資料
request.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
}
request.send(data);
}
三、回呼函數以及資料解析
根據Ajax提出者Jesse James
Garrett建議,Ajax使用XHTML+CSS來表示資訊,使用JavaScript操作DOM(Document Object
Model)進行動態顯示及互動,使用XML和XSLT進行資料交換及相關操作,由於Opera瀏覽器不支援XSLT格式對象,也不支援XSLT,所以現
在一般情況下都是使用XML進行資料傳遞。 在回呼函數中,會出現request的一些相關屬性,其代表值如下:readyState:提供當前 HTML 的就緒狀態。
0:請求未初始化
1:請求已經建立,但是還沒有發送(還沒有調用 send())
2:請求已發送,正在處理中(通常現在可以從響應中擷取內容標題)
3:請求在處理中,通常響應中已有部分資料可用了
4:響應已完成
status:提供當前HTML的狀態代碼
401:未經授權
403:禁止訪問
404:沒找到訪問頁
200:正常 回呼函數如下所示:/*
* 返回資料格式為XML的回呼函數
*/
function xmlCallBack() {
// 資料接收完成
if( request.readyState == 4 ){
// 資料正常接收
if( request.status == 200 ){
// 調用XML檔案解析函數
parseXMLMessage();
} else {
// 顯示錯誤資訊
alert("Not able to retrieve description"+request.statusText);
}
}
}
/*
* XML檔案解析函數
*/
function parseXMLMessage() {
// 擷取返回的XML檔案
var xmlDoc=request.responseXML.documentElement;
// 解析XML檔案
parseXML("elementId", xmlDoc);
}
/*
* 解析XML檔案
* @param elementId 要將資料繫結的對象Id
* @param xmlDoc 要解析的XML檔案
*/
function parseXML(elementId, xmlDoc) { // 這裡XML檔案的格式根據你的自訂,自行修改
var xmlRoot = xmlDoc.getElementsByTagName("items");
var dataType = xmlRoot[0].getAttribute("dataType");
var items = xmlDoc.getElementsByTagName('item');
switch ( dataType.toLowerCase() ) {
case "array" :
// 返回對象為結果集
bindItems(elementId, items);
break;
case "string" :
// 返回對象為字串
bindText(elementId, items[0].childNodes[0].firstChild.nodeValue);
}
} 如果Ajax調用後返回的是一個HTML頁面,則可以使用下面的這個回呼函數:/*
* HTML檔案解析函數
*/
function htmlCallBack() {
if( request.readyState == 4 ){
if( request.status == 200 ){
parseHTMLMessage();
} else {
alert("Not able to retrieve description"+request.statusText);
}
}
}
/*
* 解析HTML檔案
*/
function parseHTMLMessage() {
// 擷取返回的HTML代碼
var htmlCode = request.responseText;
// 綁定HTML代碼
bindText("elementId", htmlCode);
}
四、資料繫結
資料繫結根據不同的控制項類型,可以使用以下兩個資料繫結函數,能夠實現綁定select控制項、ul無序列表、插入HTML代碼等:/*
* 綁定結果集
*/
function bindItems(elementId, items) {
var elem = $(elementId);
// 判斷要綁定的對象,類型是否匹配
if ( elem.tagName.toLowerCase() != "select" && elem.tagName.toLowerCase() != "ul" ) {
alert("資料類型不匹配,無法進行資料繫結");
return;
}
// 綁定select
if ( elem.tagName.toLowerCase() == "select" ) {
while ( elem.childNodes.length > 0 ) {
// 清除現有資料
elem.removeChild(elem.childNodes[0]);
}
// 綁定資料
for ( var i = 0; i < items.length; i++ ) {
var option = document.createElement("OPTION");
var Data = items[i];
option.value = Data.childNodes[0].firstChild.nodeValue;
option.text = Data.childNodes[1].firstChild.nodeValue;
elem.options.add(option);
}
} else if ( elem.tagName.toLowerCase() == "ul" ) {
// 綁定ul列表
elem.innerHTML = "";
// bind data
for ( var i = 0; i < items.length; i++ ) {
var Data = items[i];
var urlAddress = Data.childNodes[0].firstChild.nodeValue;
var showText = Data.childNodes[1].firstChild.nodeValue;
var innerCode = "<li><a href=/"" + urlAddress + "/" title=/"" + showText + "/">" + showText + "</a></li>";
elem.innerHTML += innerCode;
}
}
}
/*
* 綁定字串,也可以實現綁定HTML代碼
*/
function bindText(elementId, value) {
var elem = $(elementId);
// 分析綁定物件類型
switch ( elem.tagName.toLowerCase() ) {
case "div":
case "span":
case "textarea":
elem.innerHTML = value;
break;
case "input":
elem.value = value;
break;
default:
alert("資料類型不匹配,無法進行資料繫結");
return;
}
saveHistory(elementId); // 儲存記錄用於實現瀏覽器的前進、後退按鈕
}
五、進階功能——實現Ajax提交Form表單
通過Ajax提交Form有以下兩個非常重要的地方:1、需要解析所有Form中的控制項,將控制項拼成類似於“?param1=value1¶m2=value2......”這樣的字串,然後通過“POST”模式非同步提交,將上面拼成的字串通過request.send(data)發送到伺服器。2、關於檔案上傳,由於我沒有這方面的需求,沒有寫出一個測試後的代碼,但通過查閱相關資料,看到可以通過隱藏IFrame來實現這一功能。 通過Ajax非同步提交表單的代碼如下:/*
* 通過Ajax非同步提交表單
*/
function submitFormByAjax(formId) {
sendRequestByAjax($(formId).method, $(formId).getAttributeNode("action").value, encodeFormData($(formId)), htmlCallBack);
} 解析Form內控制項,並拼成字串的函數如下:/*
* 分析Form表單資料
* @param formElement Form對象
*/
function encodeFormData(formElement) {
var whereClause = "";
var and = "";
for ( i = 0 ; i < formElement.length ; i++ ) {
var element = formElement[i];
if ( element.name != "" ) {
if (element.type=='select-one') {
element_value = element.options[element.selectedIndex].value;
} else if ( element.type == 'checkbox' || element.type == 'radio' ) {
if ( element.checked == false ) {
break;
}
element_value = trim(element.value);
} else {
element_value = trim(element.value);
}
whereClause += and + trim(element.name) + '=' + element_value.replace(//&/g,"%26");
and = "&"
}
}
return whereClause;
}
六、進階功能——實現瀏覽器的前進、後退按鈕
很多人在使用了Ajax技術後會發現,瀏覽器的前進、後退按鈕無效了,大大降低了使用者體驗,曾經這一點也被作為Ajax技
術的弊端而大範圍討論,後來經過不斷的嘗試,終於實現了這一功能,聽起來很簡答,就是通過一個隱藏的IFrame,使用JavaScript改變
IFrame的src屬性而啟用瀏覽器的前進、後退按鈕。再通過一個特殊的JavaScript函數,實現更新頁面資料。 具體的代碼如下:var historyValue = new Array(10); // 儲存記錄的最大次數
var historyCount = 0;
/*
* 儲存記錄
* @param elementId 要儲存的地區ID
*/
function saveHistory(elementId) {
// "historyFrame"隱藏的IFrame的ID屬性值
var iframeDocument = $("historyFrame");
if ( iframeDocument != null ) {
if ( historyCount == 9 ) {
historyCount = 0;
} else {
historyCount++;
}
historyValue[historyCount] = new Array(2);
historyValue[historyCount][0] = elementId;
var element = $(elementId);
historyValue[historyCount][1] = element.innerHTML;
iframeDocument.src = "/history.jsp?" + historyCount;
}
}
/*
* 擷取記錄
* @param historyIndex 記錄索引號
*/
function getHistory(historyIndex) {
if ( historyIndex != historyCount ) {
if ( historyValue[historyIndex] ) {
historyCount = historyIndex;
}
var element = $(historyValue[historyCount][0]);
element.innerHTML = historyValue[historyCount][1];
}
} history.jsp頁面中的代碼很簡單,只有以下代碼:<script>
var url=window.location.href;
if(url.indexOf('?')>-1)
{
parent.getHistory(url.substr(url.indexOf('?')+1));
document.write(window.location.search.substr(1));
}
</script> 在頁面最下端記得寫上下面的代碼:<iframe id="historyFrame" name="historyFrame" src="/history.jsp?0" height="0px" frameborder="no"></iframe>
七、總結
我認為Ajax的應用最好根據自己的需求,來實現相應的功能,盡量避免JavaScript代碼過多,拖累用戶端瀏覽器。
由於JavaScript代碼設計過於靈活,如果JavaScript程式員水平相當,肯定能夠開發出很好的JavaScript程式,如果
JavaScript程式員水平良莠不齊,最好避免多人同時開發一套JavaScript程式,防止JavaScript代碼品質降低。由於JavaScript調試起來非常不方便,建議大家多多使用FireFox瀏覽器的錯誤控制台,會給大家提供很多的方便。