張智星 jang@mirlab.org http://mirlab.org/jang 台大資工系 MIR實驗室 第十九章 AJAX 與非同步傳輸 張智星 jang@mirlab.org http://mirlab.org/jang 台大資工系 MIR實驗室
本章大綱 大綱 本章介紹如何進行網頁的非同步傳輸,特別是著重於 AJAX 的原理及範例,並說明如何以 JavaScript Framework 中最常被用到的 Prototype.js 來簡化 AJAX 的設計及應用。 主題 19-1:簡介 19-2:使用隱藏式 iframe 的非同步傳輸 19-3:基本 AJAX 原理與範例 19-4:使用 Prototype.js 來進行 AJAX 網頁設計
19-1:簡介 本小節介紹AJAX的相關知識背景。
發展緣由 Web 基本上是一個分散的系統,所有的計算和處理,都由用戶端和伺服器來共同完成,用戶端的電腦執行的是網頁中的 Client-side Scripts,而伺服器則是執行網頁中的 Server-side Scripts。 在 http 的協定下,每當使用者發出一個 Request 之後,伺服器就相執行網頁中的 Server-side Scripts (如 ASP),並將結果傳回給用戶端,再由用戶端的瀏覽器來執行網頁中的 Client-side Scripts(如 JavaScript 或 VBScript),並將結果顯示在螢幕上。 如果還要存取伺服器端的資料,就必須再一次經由表單的送出,才能指揮伺服器做事,並將結果以新的網頁資料回 傳,造成換頁,因此要保存原先網頁的資訊,比較麻煩。
非同步傳輸 目標:由 Client-side Scripts 所接收的事件(如滑鼠點選某一按鈕)來指揮 Server-side Scripts 做事,並在不換頁的情況下,將 Server-side Scripts 的執行結果悄悄地送回 Client-side Scripts 並顯示結果於同一個網頁。 可以使用以下幾種方式達成: 使用隱藏式的 iframe:這是一種比較傳統的方式,但是使用方式也比較受限。 使用 AJAX:這是一種比較常用的方式,也比較有彈性。 亦可採用微軟的 Remote Scripting 來達成非同步傳輸,但是這需要近端的機器安裝 Java,對使用者造成負擔,因此比較不實用。
AJAX AJAX 的全名是「Asynchronous JavaScript And XML」,翻成中文是「非同步式的 JavaScript 與 XML」,包含 HTML 及 CSS:負責顯示結果。 JavaScript:負責近端的事件擷取及資料處理,並大量運用 DOM (Document Object Model) 來讀取由伺服器回傳的資料,資料格式可能是 XML 或是 HTML。 XMLHttpRequest物件:負責以非同步的方式來執行遠端的程式,並接收相關的結果。
AJAX優缺點 採用 AJAX 可以提供下列好處: 也有下列幾項缺點: 簡化網頁流程設計。 降低網路資料流量。 因為不換頁,因此無法使用「上一頁」、「下一頁」來顯示所需的網頁。 搜尋引擎無法直接對動態資料建立索引,因此不利於搜尋。
19-2:使用隱藏式 iframe 的非同步傳輸 本小節介紹如何利用隱藏的iframe來接收伺服端的網頁,並將資料回傳到client端。
範例19-1 主題:使用 iframe 來進行非同步傳輸的簡單範例 說明 Webpage: remote host, local host 程式碼重點 說明 我們利用display:none將iframe隱藏,再由JavaScript指定要連到server端的位置。 此範例使用附檔名htm以顯示並沒有直接執行遠端程式碼。 下個範例是server端的處理方式。 <iframe id="myHiddenFrame" style="display:none"></iframe> var iframe = document.getElementById(‘myHiddenFrame’); iframe.src = “serverAction01.asp”;
範例19-2 主題:續19-1子網頁回傳訊息給母網頁 說明 檢視子網頁: remote host, local host 程式碼重點 子網頁會回傳訊息給隱藏iframe的母網頁。 使用 iframe 來進行非同步傳輸,是一個簡單可行的方式,足以對付一般基本應用。但是此方法最大的缺點,是子網頁無法支援 post 的資料傳送方式。 window.parent.showRetrievedInfo('<%=serverName%>', '<%=serverUrl%>', '<%=serverIp%>');
基本流程總結 母網頁利用隱藏的iframe(子網頁)來即時執行伺服器端的程式。 子網頁將結果回傳給母網頁的函式。 母網頁的函式藉由innerHTML的屬性,來修改<div>標籤內容。 iframe.src = “Server端網頁”; window.parent.函式(<%ASP參數%>); document.getElementById(‘DIV標籤ID’).innerHTML=顯示內容
範例19-3 主題:使用隱藏式iframe來查詢資料庫 說明 Webpage: remote host, local host 程式碼重點 由於 SQL 指令含有空格,因此在使用 get 方式來設定網址時,我們必須先將 SQL 指令送到 escape() 函數,以便進行適當之編碼來避開可能造成錯誤之字元。 由於我們必須反覆用到資料庫的查詢,達成此功能的相關函數是 getQueryResult(),定義於 sqlUtility.fun。 <script runat=server language=javascript src="sqlUtility.fun"></script>
範例19-4 主題:查詢資料庫Server端網頁 說明 檢視子網頁: remote host, local host 程式碼重點 由於我們不希望使用者去修改資料庫,因此會使用通用運算式來檢查 SQL 指令,不是 "select" 開頭,則認定是不合法的 SQL 指令並回傳警告訊息「SQL command not started with SELECT is disabled!」。 if (sql.search(/^select/i)<0)
19-3:基本 AJAX 原理與範例 本小節介紹AJAX的基本原理以及如何應用。
AJAX 的使用方式 主要包含三個基本步驟: 近端(用戶端)的發送函數:負責在接收主網頁的事件後,設定 AJAX 物件,並對伺服器發送 request 以啟動伺服器端程式碼的執行。 遠端(伺服器端)的程式碼:通常是一個 ASP 網頁,負責在伺服器執行必要之步驟,例如檢查帳號密碼,或是對資料庫進行查詢等。 近端(用戶端)的接收函數:負責接收伺服器的執行結果,並將結果以非同步的方式顯示在主網頁上。
範例19-5 主題:介紹AJAX使用原理的基本範例 說明 Webpage: remote host, local host 近端的發送函數:getServerTime()。 遠端的程式碼:showTime.asp。 近端的接收函數:displayTime()。 以下會分別說明這個範例的原理。
近端發送函數 近端的發送函數是 getServerTime(),主要負責當使用者點選按鈕後,產生 AJAX 物件並設定之,然後對伺服器發出 request 我們使用下列產生AJAX物件 對此物件設定接收伺服器的設定函式 使用下列方式來設定 AJAX 物件的其它性質 最後,送出AJAX命令 ajax = new ActiveXObject("Msxml2.XMLHTTP"); ajax.onReadyStateChange=displayTime; ajax.open(“GET”, url, true);//傳遞方式, 網址, 是否為非同步 ajax.send("");
範例19-6 主題:範例19-5的遠端程式碼 說明 連結:檢視原始檔 程式碼重點 本例功能相當簡單,只是印出現在的時間 today=new Date(); time=today.toString(); Response.write("<font color=red>"+time+"</font>");
近端的接收函數(1) 檢查伺服器程式碼是否執行完畢 說明 readyState 代表 ajax 目前的狀態 if (ajax.readyState==4) readyState 的值 說明 尚未啟始 1 已經建立連結 2 遠端已經收到要求 3 遠端程式碼處理中 4 處理完畢
近端的接收函數(2) 檢查伺服器回應函數是否執行無誤 只有當網頁 showTime.asp 回傳的狀態碼是 200 時,才代表 showTime.asp 的執行無誤。 if (ajax.status==200)
近端的接收函數(3) 將 AJAX 回傳的資訊指定給 <DIV>區塊內容 伺服器程式碼回傳的原始資訊,會原封不動地放到 responseText 此性質中。如果伺服器回傳的資訊是 XML 文件格式,我們可以由另一個性質 responseXML 來讀取 XML 文件內的各個物件。 document.getElementById('showResult').innerHTML = ajax.responseText;
19-4:使用 Prototype.js 來進行 AJAX 網頁設計
Prototype.js 目前在網路上最常被用到的 JavaScript framework,就是 prototype.js,可由網址下載: http://prototype.conio.net 所謂的 framework,可以看成是原先 JavaScript 的應用程式介面(API,或是 Appliation Program Interface),可以根基於基本的 JavaScript 來提供更先進的功能。 prototype.js所提供的擴充功能如下: AJAX:提供對於 AJAX 的支援,簡化了使用方式及流程,也提高了 AJAX 在各種不同瀏覽器的相容性。 DOM:延伸了 DOM 的物件和功能。 JSON(JavaScript Object Notation):支援 JSON 的資料格式,比 XML 易於瞭解與讀寫。
範例19-7 主題:使用Prototype.js實作AJAX範例 說明 Webpage: remote host, local host 資料庫連結:test.mdb 這是一個比較老的資料庫,你也可以輸入其他歌手看看,例如周華健、巫啟賢、林憶蓮、莫文蔚、陶晶瑩等。
近端發送函數(1) 近端的發送函數:sendQuery() 此函數定義了遠端的程式碼網頁 queryDb01.asp,同時也加入了參數列 singerName=鄧麗君,最後產生 AJAX 物件並定義相關參數,送到遠端執行。其中有幾點要注意: 「$F('singerName')」是 Prototype.js 所提供的簡化語法,其功能全等於「document.getElementById('singerName').value」。 如果我們在 $() 之內輸入多個 id,此函數會回傳對應這些 id 的物件所成的陣列。
近端發送函數(2) 產生 AJAX 物件的第二個參數是 此參數全等於一個具有三個欄位的物件,因此我們甚至可以將 sendQuery() 函數的第三列敘述: 改成: method:‘post’, parameters:queryString, onComplete:showQueryResult var ajax = new Ajax.Request(url, {method:'post', parameters:queryString, onComplete:showQueryResult}); var ajaxParam = new Object(); ajaxParam.method='post'; ajaxParam.parameters=queryString; ajaxParam.onComplete=showQueryResult; var ajax = new Ajax.Request(url, ajaxParam);
範例19-8 主題:範例19-7的遠端程式碼 說明 連結:檢視原始檔 程式碼重點 此程式碼接收使用者輸入的歌手名字,造出 SQL 命令,並送入資料庫查詢,並將查詢結果印出來。 database="test.mdb"; sql="SELECT * from Singer where ChineseName = ‘“ +Request("singerName")+"'“; outStr=getFirstRecordFromQueryResult(database, sql); Response.write(outStr);
近端接收函數 近端的接收函數:showQueryResult() 在此函數中,我們只使用一列程式碼: 這也是 Prototype.js 所提供的簡化語法,其功能全等於 : 由於資料庫和網頁編碼的不一致,常會導致亂碼的產生,因此我們這個範例的兩個檔案(ajaxViaPrototype01.htm 和queryDb01.asp),都是採用 UTF-8 的編碼,一般常用的文字編輯器,例如記事本(notepad.exe)及 UltraEdit,都支援 UTF-8 文字檔的檢視和編輯。 Element.update('queryResult', xmlHttpObj.responseText); document.getElementById('queryResult').innerHTML= xmlHttpObj.responseText
範例19-9 主題:使用prototype.js中的Updater()來達成AJAX 說明 Webpage: remote host, local host 程式碼重點 說明 Ajax.Updater() 的第一個參數是 ‘queryResult’,這就是代表回傳資料將會被指定到 document.getElementById(‘queryResult’).innerHTML,因此我們就不必另外在寫一個負責顯示結果的函數。 var ajax = new Ajax.Updater('queryResult', url, {method:'post', parameters:queryString});
範例19-10 主題:使用Responders.register做出請稍候的效果 說明 Webpage: remote host, local host 程式碼重點 說明 onLoading 事件的函數可以不斷印出「資料處理中,請稍後...」之訊息,而對應至 onComplete 事件的函數則可以顯示最後查詢結果。 Ajax.Responders.register ({ onLoading: function(){ Element.update(‘queryResult’, ‘<font color=red>查詢資料中,請稍候...</font>');}, onComplete: function(junk, xmlHttpObj){ Element.update('queryResult', xmlHttpObj.responseText);}});