如何掌握AJAX之AJAX通訊技術(shù)_AJAX教程
推薦:如何用AJAX打造博客無刷新搜索如果你對AJAX不是很了解,可以先看看這篇教程: 《AJAX初體驗之上手篇》 。 現(xiàn)在博客很流行,相信應(yīng)該上網(wǎng)時間稍微長點的朋友都會在這或者在那的有一個自己的博客。對于一些有一定能力的朋友,可能更喜歡自己去下載一個博客程序來架設(shè)一個自己的博客,而不是
當(dāng)在網(wǎng)上沖浪時,將在瀏覽器和服務(wù)器之間存在大量的請求。最初,所有的這種請求都是在用戶做出需要這一步驟的明顯操作時發(fā)生的。Ajax技術(shù)將開發(fā)人員從等待用戶做出這樣的操作中解放出來,允許他在任何時間創(chuàng)建一個對服務(wù)器的調(diào)用。Ajax通信支持許多不同的技術(shù)。每一種技術(shù)都有自己的優(yōu)點和缺點,因此了解什么情況使用哪一種技術(shù)是很重要的。
隱藏幀技術(shù)
隨著HTML幀的引入,隱藏幀(hidden frame)技術(shù)也應(yīng)運而生了。該技術(shù)后面的基本想法是創(chuàng)建一個幀集,其中包含用于客戶端—服務(wù)器通信的隱藏幀。可以通過將幀的寬度或高度設(shè)置為0像素來隱藏一個幀,以使其不顯示。盡管一些早期的瀏覽器(諸如Netscape 4)不能夠完全隱藏幀,經(jīng)常會留下一些明顯的幀邊框,但該技術(shù)還是廣泛地為開發(fā)人員所采用。
1. 模式
隱藏幀技術(shù)遵循一種特定的四步模式(參見圖2-1)。第一步總是從一個與用戶交互的Web頁面中的可見幀開始的。顯然,用戶并不知道隱藏幀的存在(在現(xiàn)代瀏覽器中,它是不顯示的),以通常的形式與網(wǎng)頁進行交互。在某些時間,用戶執(zhí)行了一個需要從服務(wù)器獲取額外數(shù)據(jù)的操作。當(dāng)這個操作發(fā)生時,第一步就發(fā)生了:產(chǎn)生一個對隱藏幀的JavaScript函數(shù)調(diào)用。這個調(diào)用可以簡單地將隱藏幀重定向到另一個頁面,或者復(fù)雜地傳送表單數(shù)據(jù)。不管這個函數(shù)有多復(fù)雜,其結(jié)果都是產(chǎn)生第2步:向服務(wù)器發(fā)送一個請求。
圖 2-1
該模式中的第3步是從服務(wù)器上接收一個響應(yīng)。由于處理的是幀,因此該響應(yīng)必然是另一個網(wǎng)頁。該網(wǎng)頁必須包含從服務(wù)器返回的所請求的數(shù)據(jù),同時一些JavaScript將把這些數(shù)據(jù)傳給可見的幀。通常,這是通過在返回的網(wǎng)頁中分配一個onload事件處理函數(shù)(event handler)做到的,該網(wǎng)頁在其全部載入之后調(diào)用可見幀中的函數(shù)(這就是第4步)。當(dāng)數(shù)據(jù)位于可見幀中后,該幀就可以決定如何處理這些數(shù)據(jù)了。
2. 隱藏幀的GET請求
我們已經(jīng)闡述了隱藏幀技術(shù)的基本原理,現(xiàn)在將更深入地研究它。對于任何一種新技術(shù),最好的方法就是通過具體的實例來學(xué)習(xí)。在該實例中,將創(chuàng)建一個簡單的查詢頁面,客戶服務(wù)代表通過該頁面可以查詢客戶的信息。由于這是本書的第一個例子,因此它十分的簡單:用戶輸入客戶ID,然后接收與該客戶相關(guān)的信息。由于該功能通常需要數(shù)據(jù)庫支持,因此還必須做一些服務(wù)器端的開發(fā)。該例子使用的是PHP——這是一種優(yōu)秀的開源服務(wù)端語言,還將使用到MySQL(在從www.mysql.org下載)——這是一種與PHP結(jié)合得很好的開源數(shù)據(jù)庫。盡管本例確定為使用MySQL,但只需少量的修改就可以在其他數(shù)據(jù)庫上運行。
首先,在實現(xiàn)客戶資料查詢之前,你必須有一個包含該信息的數(shù)據(jù)庫表。可以使用以下SQL腳本來創(chuàng)建一個客戶表:
`CustomerId` int(11) NOT NULL auto_increment,
`Name` varchar(255) NOT NULL default '',
`Address` varchar(255) NOT NULL default '',
`City` varchar(255) NOT NULL default '',
`State` varchar(255) NOT NULL default '',
`Zip` varchar(255) NOT NULL default '',
`Phone` varchar(255) NOT NULL default '',
`E-mail` varchar(255) NOT NULL default '',
PRIMARY KEY (`CustomerId`)
) TYPE=MyISAM COMMENT='Sample Customer Data';
在這張數(shù)據(jù)庫表中最重要的字段是CustomerId,我們將通過它來查詢客戶信息。你可以在www.wrox.com下載這個腳本以及一些測試數(shù)據(jù)。當(dāng)建好數(shù)據(jù)庫表后,就可以將精力轉(zhuǎn)到HTML代碼上了。要使用隱藏幀技術(shù),首先必須創(chuàng)建一個HTML幀集,例如:
<frame name="displayFrame" src="display.htm" noresize="noresize" />
<frame name="hiddenFrame" src="about:blank" noresize="noresize" />
</frameset>
這部分代碼中最重要的是<frameset/>元素的rows屬性。通過將其設(shè)置為100%,0,瀏覽器就知道不顯示名為hiddenFrame的第二個幀了。緊接著,將frameborder屬性設(shè)置為0則是確保每個幀都沒有可見的邊框。在幀集聲明中最后一個重要的步驟是為每個幀設(shè)置noresize屬性,使得用戶不可能在不經(jīng)意間調(diào)整幀的大小而發(fā)現(xiàn)隱藏幀,隱藏幀的內(nèi)容永遠不會成為可顯示的用戶界面的一部分。接下來要處理的是一個請求和顯示客戶信息的頁面。這是一個相對簡單的頁面,由一個用來輸入客戶ID的文本框,一個執(zhí)行請求的按鈕,以及用來顯示查詢到的客戶信息的<div>元素所組成:
<p>Customer ID: <input type="text" id="txtCustomerId" value="" /></p>
<p><input type="button" value="Get Customer Info"
onclick="requestCustomerInfo()" /></p>
<div id="divCustomerInfo"></div>
注意,按鈕調(diào)用的是名為requestCustomerInfo()的函數(shù),該函數(shù)將負責(zé)與隱藏幀交互以獲取數(shù)據(jù)。它將獲取文本框中的值,將其添加到getcustomerdata.php的查詢字符串上,以getcustomerdata.php?id=23的格式創(chuàng)建一個URL。然后將這個URL指派給隱藏幀,以下就是這個函數(shù)的代碼:
var sId = document.getElementById("txtCustomerId").value;
top.frames["hiddenFrame"].location = "getcustomerdata.php?id=" + sId;
}
該函數(shù)的第一步是從文本框中獲取客戶標(biāo)識號("txtCustomerId")。這是將文本框的ID txtCustomerId作為參數(shù),調(diào)用document.getElementById()函數(shù),并獲取返回的value屬性(value屬性保存了文本框中的文本內(nèi)容)來實現(xiàn)的。然后,將這個ID添加到字符串getcustomerdata.php?id=之后生成完整的URL。第二行代碼則是創(chuàng)建此URL并將其賦給隱藏幀。為了獲得對隱藏幀的引用,首先要使用top對象來獲取瀏覽器的頂級窗口(topmost window)。該對象擁有一個frames數(shù)組,在其中可以找到這個隱藏幀。由于每個幀都是一個窗口對象,因此可以將其位置設(shè)置為預(yù)期的URL。
這是發(fā)出請求所需的所有信息。注意,由于這是一個GET請求(通過一個查詢字符串傳遞信息),因此是很簡單的。(很快,你將看到如何使用隱藏幀技術(shù)來執(zhí)行一個POST請求。)除了requestCustomerInfo()函數(shù)之外,還需要另一個在查詢后顯示客戶信息的函數(shù)。當(dāng)數(shù)據(jù)返回時,隱藏幀將調(diào)用這個displayCustomerInfo()函數(shù),其唯一的參數(shù)是包含要顯示的客戶數(shù)據(jù)的字符串:
var divCustomerInfo = document.getElementById("divCustomerInfo");
divCustomerInfo.innerHTML = sText;
}
在這個函數(shù)中,第一行代碼將查詢對用于數(shù)據(jù)顯示的<div/>元素的引用。第二行代碼將把包含客戶信息的字符串(sText)的值賦給<div/>元素的innerHTML屬性。使用innerHTML屬性,可以將HTML嵌入到格式化的字符串中。這將由主顯示頁面的代碼來完成。現(xiàn)在我們將創(chuàng)建服務(wù)器端程序邏輯。getcustomerdata.php中的基本代碼是在基本的HTML頁面上添加兩處PHP代碼:
<head>
<title>Get Customer Data</title>
<?php
//php代碼
?>
</head>
<body>
<div id="divInfoToReturn"><?php echo sInfo ?></div>
</body>
</html>
在該頁面中,第一個PHP代碼塊將包括查詢客戶數(shù)據(jù)的邏輯(很快將討論到)。而第二個PHP代碼塊則負責(zé)將包含客戶數(shù)據(jù)的sInfo變量的值輸出到<div/>元素中。從這個<div/>元素中,你可以讀取該數(shù)據(jù)并將數(shù)據(jù)傳送給顯示幀。為此,需要創(chuàng)建在頁面完全載入后調(diào)用的JavaScript函數(shù)。
var divInfoToReturn = document.getElementById("divInfoToReturn");
top.frames["displayFrame"].displayCustomerInfo(divInfoToReturn.innerHTML);
};
該函數(shù)將直接賦給window.onload事件處理函數(shù)中。它首先獲取對包含客戶信息的<div/>元素的引用,然后使用數(shù)組top.frames訪問顯示幀,并調(diào)用前面定義的display CustomerInfo()函數(shù),將其傳給<div/>元素的innerHTML屬性。這就是所有與發(fā)送該信息相關(guān)的JavaScript。但首先如何獲取這些信息呢?需要一些PHP代碼來從數(shù)據(jù)庫查詢信息。在PHP代碼中的第一步是定義所有需要的數(shù)據(jù)塊。在本例中,這些數(shù)據(jù)塊包括用來查詢的客戶ID、返回信息的sInfo變量,以及訪問數(shù)據(jù)庫所需要的信息(數(shù)據(jù)庫服務(wù)器、數(shù)據(jù)庫名、用戶名、密碼以及SQL查詢字符串):
sID = _GET["id"];
sInfo = "";
sDBServer = "your.databaser.server";
sDBName = "your_db_name";
sDBUsername = "your_db_username";
sDBPassword = "your_db_password";
sQuery = "Select * from Customers where CustomerId=".sID;
//更多代碼
?>
這段代碼首先從查詢字符串中獲取id參數(shù)。為了便于獲取,PHP將所有的查詢字符串參數(shù)組織于-GET數(shù)組中。這個id存儲在sID中,它將用來創(chuàng)建存儲于sQuery中的SQL查詢字符串。在此還將創(chuàng)建sInfo變量,并將其設(shè)置為空字符串。在這段代碼中的所有其他變量,都包含了指定特定數(shù)據(jù)庫配置的信息,根據(jù)你自己的實現(xiàn)環(huán)境將其替換為正確的值。獲取了用戶的輸入,做好了連接數(shù)據(jù)庫的基本準備,下一步就是創(chuàng)建數(shù)據(jù)庫連接,執(zhí)行查詢,返回結(jié)果。如果存在一個指定ID的客戶,sInfo將填入包含所有數(shù)據(jù)的HTML字符串,包括對電子郵件地址創(chuàng)建一個鏈接,如果客戶ID是無效的,那么sInfo將填入錯誤消息,以傳給顯示幀:
sID = _GET["id"];
sInfo = "";
sDBServer = "your.databaser.server";
sDBName = "your_db_name";
sDBUsername = "your_db_username";
sDBPassword = "your_db_password";
sQuery = "Select * from Customers where CustomerId=".sID;
oLink = mysql_connect(sDBServer,sDBUsername,sDBPassword);
@mysql_select_db(sDBName) or sInfo="Unable to open database";
if(oResult = mysql_query(sQuery) and mysql_num_rows(oResult) > 0) {
aValues = mysql_fetch_array(oResult,MYSQL_ASSOC);
sInfo = aValues['Name']."<br />".aValues['Address']."<br />".
aValues['City']."<br />".aValues['State']."<br />".
aValues['Zip']."<br /><br />Phone: ".aValues['Phone']."<br />".
"<a href=\"mailt".aValues['E-mail']."\">".
aValues['E-mail']."</a>";
} else {
sInfo = "Customer with ID sID doesn't exist.";
}
mysql_close(oLink);
?>
突出顯示的頭兩行代碼用來完成從PHP到MySQL數(shù)據(jù)庫的連接。緊接著,調(diào)用mysql_ query()函數(shù)來執(zhí)行SQL查詢。如果函數(shù)返回結(jié)果,并且該結(jié)果至少包括一行,那么程序?qū)@取該信息,并將其存入變量sInfo中;否則,sInfo將填入一個錯誤消息。最后兩行則負責(zé)釋放數(shù)據(jù)庫連接。
關(guān)于更復(fù)雜的PHP和MySQL編程的闡述已超出了本文討論的范圍。現(xiàn)在當(dāng)sInfo輸出到<div/>元素時,它將包含正確的信息。onload事件處理函數(shù)將讀取這些數(shù)據(jù),然后將其發(fā)送到顯示幀上。如果查詢到客戶,其相應(yīng)的信息將會顯示出來,如圖2-2所示。另一方面,如果客戶不存在,則會在屏幕的相同位置顯示錯誤消息。無論如何,客戶服務(wù)代表都將獲得一個很好的用戶體驗。你的第一個Ajax程序也就完成了。
圖 2-2
3. 隱藏幀的POST請求
前面的例子使用GET請求來從數(shù)據(jù)庫中獲取信息。由于客戶ID能夠以查詢字符串的形式添加到URL中,因此十分簡單。但如果需要發(fā)送POST請求該怎么辦呢?它也可以使用隱藏幀技術(shù),不過需要一些額外的工作。POST請求通常是用于向服務(wù)器發(fā)送數(shù)據(jù)的場合,而與GET請求僅從服務(wù)器上獲取數(shù)據(jù)不同。盡管GET請求可以通過查詢字符串來向服務(wù)器發(fā)送額外的數(shù)據(jù),但一些瀏覽器最多只能夠處理512KB以內(nèi)的查詢字符串信息。對于POST請求而言,則可以發(fā)送2GB的信息,能夠良好地滿足絕大多數(shù)的應(yīng)用。
從傳統(tǒng)意義上說,只能夠通過將表單的method屬性設(shè)置為post來發(fā)送POST請求。然后,包含在表單中的數(shù)據(jù)就會通過POST請求發(fā)送到action屬性中指定的URL上。更復(fù)雜的問題是當(dāng)表單提交之后,將會從當(dāng)前頁跳轉(zhuǎn)到一個新的URL上,這與Ajax的目的是背道而馳的。但萬幸的是,可以通過表單中一個不太知名的target屬性來簡單實現(xiàn)。<form/>元素的target屬性的功能從某種意義上說與<a/>元素的target屬性的功能類似:用來指定跳轉(zhuǎn)的目的URL。通過設(shè)置表單元素的target屬性,可以有效地使得在其他幀或窗口(在本例中是隱藏幀)中顯示出表單的提交結(jié)果之后,表單頁面仍然保持不變。首先重新定義一個幀集。與上一個例子唯一不同的是可見幀包含了用來輸入客戶數(shù)據(jù)的表單:
<frame name="displayFrame" src="entry.htm" noresize="noresize" />
<frame name="hiddenFrame" src="about:blank" noresize="noresize" />
</frameset>
輸入表單的內(nèi)容包含在一個<form/>元素中,而且針對保存在數(shù)據(jù)庫中的每個字段都有一個相應(yīng)的文本框(除了自動生成的客戶ID之外)。同樣也有一個<div/>元素,用來顯示與客戶端—服務(wù)器通信相關(guān)的狀態(tài)信息:
<p>Enter customer information to be saved:</p>
<p>Customer Name: <input type="text" name="txtName" value="" /><br />
Address: <input type="text" name="txtAddress" value="" /><br />
City: <input type="text" name="txtCity" value="" /><br />
State: <input type="text" name="txtState" value="" /><br />
Zip Code: <input type="text" name="txtZipCode" value="" /><br />
Phone: <input type="text" name="txtPhone" value="" /><br />
E-mail: <input type="text" name="txtEmail" value="" /></p>
<p><input type="submit" value="Save Customer Info" /></p>
</form>
<div id="divStatus"></div>
注意,<form/>元素的target屬性也設(shè)置為hiddenFrame,因此當(dāng)用戶點擊該按鈕時,提交的結(jié)果將顯示在隱藏幀中。在本例中,主頁面中只需要一個JavaScript函數(shù):savaResult()。當(dāng)隱藏幀返回客戶數(shù)據(jù)保存結(jié)果時,將調(diào)用該函數(shù):
var divStatus = document.getElementById("divStatus");
divStatus.innerHTML = "Request completed: " + sMessage;
}
隱藏幀的職責(zé)是向該函數(shù)傳遞一個消息,該消息將顯示給用戶。它可能是信息已保存的確認信息,或者是說明為什么保存失敗的錯誤信息。接下來處理POST請求的文件是SavaCustomer.php。與前一個例子一樣,該頁面也是由簡單的HTML頁面加上一些PHP和JavaScript代碼組成的。其中PHP代碼用來從請求中收集信息,然后將其保存到數(shù)據(jù)庫中。由于這是一個POST請求,因此_POST數(shù)組中包含了提交的所有信息:
sName = _POST["txtName"];
sAddress = _POST["txtAddress"];
sCity = _POST["txtCity"];
sState = _POST["txtState"];
sZipCode = _POST["txtZipCode"];
sPhone = _POST["txtPhone"];
sEmail = _POST["txtEmail"];
sStatus = "";
sDBServer = "your.database.server";
sDBName = "your_db_name";
sDBUsername = "your_db_username";
sDBPassword = "your_db_password";
sSQL = "Insert into Customers(Name,Address,City,State,Zip,Phone,`E-mail`) ".
" values ('sName','sAddress','sCity','sState', 'sZipCode'".
", 'sPhone', 'sEmail')";
//更多代碼
?>
這個代碼片段將獲取與客戶相關(guān)的所有POST信息;此外,還定義了一個狀態(tài)消息(sStatus)以及所需的數(shù)據(jù)庫信息(與上一個例子相同)。這里的SQL語句是一個INSERT語句,它將獲取的信息添加到數(shù)據(jù)庫中。
執(zhí)行這個SQL語句的代碼與上一個例子十分類似:
sName = _POST["txtName"];
sAddress = _POST["txtAddress"];
sCity = _POST["txtCity"];
sState = _POST["txtState"];
sZipCode = _POST["txtZipCode"];
sPhone = _POST["txtPhone"];
sEmail = _POST["txtEmail"];
sStatus = "";
sDBServer = "your.database.server";
sDBName = "your_db_name";
sDBUsername = "your_db_username";
sDBPassword = "your_db_password";
sSQL = "Insert into Customers(Name,Address,City,State,Zip,Phone,`E-mail`) ".
" values ('sName','sAddress','sCity','sState', 'sZipCode'".
", 'sPhone', 'sEmail')";
oLink = mysql_connect(sDBServer,sDBUsername,sDBPassword);
@mysql_select_db(sDBName) or sStatus = "Unable to open database";
if(oResult = mysql_query(sSQL)) {
sStatus = "Added customer; customer ID is ".mysql_insert_id();
} else {
sStatus = "An error occurred while inserting; customer not saved.";
}
mysql_close(oLink);
?>
在此,mysql_query()函數(shù)的結(jié)果只是一個表示語句執(zhí)行成功的指示器。如果執(zhí)行成功, sStatus變量中將填入一個消息,表明保存已經(jīng)成功,并返回為該數(shù)據(jù)指定的客戶ID。mysql_ insert_id()函數(shù)始終返回在最新的INSERT語句返回值的基礎(chǔ)上自動遞增的值。如果因為某些原因,該語句沒有成功執(zhí)行,sStatus變量將填入一個錯誤消息。sStatus變量將輸出到一個在載入窗口時運行的JavaScript函數(shù)中:
window.onload = function () {
top.frames["displayFrame"].saveResult("<?php echo sStatus ?>");
}
</script>
這段代碼調(diào)用了savaResult()函數(shù),該函數(shù)定義于顯示幀中,傳入的參數(shù)值是PHP變量sStatus。由于該變量包含一個字符串,因此必須將PHP的echo語句放在引號中。當(dāng)執(zhí)行該函數(shù)時,假設(shè)客戶數(shù)據(jù)已保存,則輸入表單頁面看起來如圖2-3所示。
圖 2-3
當(dāng)執(zhí)行這段代碼之后,你還可以自由地使用同樣的表單向數(shù)據(jù)庫中添加更多客戶,因為它不再消失。
4. 隱藏iFrame
新一代的客戶端—服務(wù)器通信模式幕后所采用的是iframe,它是在HTML 4.0中引入的。iframe與幀基本是相同的,唯一的區(qū)別是iframe可以放在一個未設(shè)置幀集的HTML頁面中,可以使頁面中的任意部分成為一個幀。iframe技術(shù)可以在未預(yù)先設(shè)置幀集的頁面中使用,能夠更好地適應(yīng)于功能的逐漸添加。iframe甚至還可以使用JavaScript在運行時創(chuàng)建,為了簡單起見,語義化HTML(semantic HTML)支持使瀏覽器將Ajax功能看作是一個有益的增強(這將在稍后討論)。由于可以用與普通幀相同的方法使用和訪問iframe,因此它們都是Ajax通信的理想選擇。發(fā)揮iframe的優(yōu)勢有兩種方法。最簡單的方法是在頁面中簡單地嵌入iframe,并像隱藏幀那樣用來發(fā)出請求。為此,第一個例子中的顯示頁面將修改為:
<p>Customer ID: <input type="text" id="txtCustomerId" value="" /></p>
<p><input type="button" value="Get Customer Info"
onclick="requestCustomerInfo()" /></p>
<div id="divCustomerInfo"></div>
<iframe src="about:blank" name="hiddenFrame" width="0" height="0"
frameborder="0"></iframe>
注意,這個iframe中的width、height和frameborder屬性都設(shè)置成了0,這可將其從視線中隱去。由于iframe的名字仍是hiddenFrame,所以這個頁面的JavaScript代碼可以如前一樣正常工作。不過,對于GetCustomerData.php頁面還需要做一些小的修改。在該頁面中的JavaScript函數(shù)先前是在名為displayFrame的幀中查找displayCustomerInfo()函數(shù)。如果你使用該技術(shù),又不存在該名字的幀,則必須修改代碼,用parent來代替它:
var divInfoToReturn = document.getElementById("divInfoToReturn");
parent.displayCustomerInfo(divInfoToReturn.innerHTML);
};
現(xiàn)在這個例子能夠和本文中的第一例子一樣正常工作了。
第二種使用隱藏iframe的方法是通過JavaScript動態(tài)地創(chuàng)建它們。由于并非所有瀏覽器實現(xiàn)iframe的方法都是一樣的,所以需要一些技巧,使得它有助于一步步地創(chuàng)建隱藏的iframe。
第一步很簡單,使用document.createElement()方法創(chuàng)建iframe并賦予必要的屬性:
var oIFrameElement = document.createElement("iframe");
oIFrameElement.width=0;
oIFrameElement.height=0;
oIFrameElement.frameBorder=0;
oIFrameElement.name = "hiddenFrame";
oIFrameElement.id = "hiddenFrame";
document.body.appendChild(oIFrameElement);
//更多代碼
}
本段代碼的最后一行很重要,因為它將iframe添加到document結(jié)構(gòu)中;沒有添加到document中的iframe是無法執(zhí)行請求的。另外注意,該iframe的name和id屬性都是設(shè)置為hiddenFrame。這是必要的,因為有些瀏覽器是通過name屬性訪問新的幀,而有些則是通過id屬性新的幀。緊接著定義一個全局變量,用來保存對該幀對象的引用。注意,針對iframe元素的這個幀對象并非是從createElement()函數(shù)返回的。要獲得該對象,必須從幀集合中獲取。以下就是即將保存在全局變量中的內(nèi)容:
function createIFrame() {
var oIFrameElement = document.createElement("iframe");
oIFrameElement.width=0;
oIFrameElement.height=0;
oIFrameElement.frameBorder=0;
oIFrameElement.name = "hiddenFrame";
oIFrameElement.id = "hiddenFrame";
document.body.appendChild(oIFrameElement);
oIFrame = frames["hiddenFrame"];
}
如果你將這些代碼放到前面的iframe例子中,那么需要對requestCustomerInfo()函數(shù)進行如下修改:
if (!oIFrame) {
createIFrame();
setTimeout(requestCustomerInfo, 10);
return;
}
var sId = document.getElementById("txtCustomerId").value;
oIFrame.location = "GetCustomerData.php?id=" + sId;
}
基于這些修改,該函數(shù)將會檢查oIFrame是否為空。如果為空,則調(diào)用createFrame(),并會為該函數(shù)的調(diào)用設(shè)置10ms的超時時間。這是很必要的,因為只有IE瀏覽器能夠立即識別插入的iframe,大部分其他瀏覽器需要花幾毫秒來識別它,以允許通過它發(fā)送請求。當(dāng)再次執(zhí)行該函數(shù)時,將執(zhí)行代碼的其余部分,其中最后一行已經(jīng)修改為對OIFrame對象的引用。盡管該技術(shù)能夠很容易地應(yīng)用于GET請求,但POST請求卻完全不同。只有一部分瀏覽器允許你設(shè)置表單的target屬性來動態(tài)創(chuàng)建iframe;但IE并不是其中的一種。因此,要使用隱藏iframe技術(shù)來發(fā)送POST請求還需要一些技巧。
5. 隱藏iframe的POST請求
要使用隱藏iframe來完成POST請求,其方法是在隱藏幀中載入一個包含表單的頁面,用數(shù)據(jù)填充該表單,然后再提交該表單。當(dāng)這個可見的表單(你實際輸入數(shù)據(jù)的那個)提交時,必須取消這次提交而將信息轉(zhuǎn)發(fā)給隱藏幀。為此,必須定義一個函數(shù),用來處理iframe的創(chuàng)建以及隱藏表單的載入:
if (!oIFrame) {
createIFrame();
}
setTimeout(function () {
oIFrame.location = "ProxyForm.htm";
}, 10);
}
這個名為checkIFrame()的函數(shù)首先檢查隱藏的iframe是否已經(jīng)創(chuàng)建。如果沒有,則調(diào)用createIFrame()。然后,在將iframe的地址設(shè)置為ProxyForm.htm(這是一個隱藏表單頁面)之前,為其設(shè)置一個超時值。由于該函數(shù)調(diào)用需要花一些時間,而重要的是該頁面每次加載時都將提交該表單。ProxyForm.htm文件很簡單,只包括很少的JavaScript,用來提示主頁面已經(jīng)裝載完成:
<head>
<title>Proxy Form</title>
<script type="text/javascript">
window.onload = function () {
parent.formReady();
}
</script>
</head>
<body>
<form method="post"></form>
</body>
</html>
正如你所見,該頁面的主體只包含一個空的表單,而標(biāo)題中只包含一個onload事件處理函數(shù)。當(dāng)載入該頁面時,頁面將通過調(diào)用parent.formReady()來使主頁面知道它已經(jīng)做好接收請求的準備。而formReady()函數(shù)則是包含在主頁面本身中的,類似于:
var oHiddenForm = oIFrame.document.forms[0];
var oForm = document.forms[0];
for (var i=0 ; i < oForm.elements.length; i++) {
var oHidden = oIFrame.document.createElement("input");
oHidden.type = "hidden";
oHidden.name = oForm.elements[i].name;
oHidden.value = oForm.elements[i].value;
oHiddenForm.appendChild(oHidden);
}
oHiddenForm.action = oForm.action;
oHiddenForm.submit();
};
在該函數(shù)中的第一步是獲取對隱藏iframe中表單的引用,可以通過訪問該幀的document.forms集合來獲取。由于在該頁面中只有一個表單,因此可以安全地從該集合中獲得第一個表單(即索引值為0),并將其存儲于oHiddenForm中。然后,將對主頁面表單的引用存于oForm中。緊接著,一個for循環(huán)對主頁面中該表單的各元素進行遍歷(使用elements集合)。對于表單中的每一個元素,都將在隱藏幀(注意,必須使用oIFrame.document.createElement()而不只是document.createElement())中創(chuàng)建一個隱藏的輸入元素。這個隱藏的輸入元素擁有與該表單元素相同的名字和值,然后使用appendChild()函數(shù)將其添加到隱藏的表單中。
當(dāng)所有的表單元素都添加完后,隱藏的表單還將設(shè)置與主頁面表單相同的action。通過從表單中讀取action來取代硬編碼,就可以在任何頁面中使用formReady()。該函數(shù)的最后一步是提交這個隱藏的表單。剩下的最后一件事就是確保主頁面的表單不以通常的方式提交自己。要達到這一目標(biāo),只需在onsubmit事件處理函數(shù)中調(diào)用checkIFrame()并返回false:
onsubmit="checkIFrame();return false">
<p>Enter customer information to be saved:</p>
<p>Customer Name: <input type="text" name="txtName" value="" /><br />
Address: <input type="text" name="txtAddress" value="" /><br />
City: <input type="text" name="txtCity" value="" /><br />
State: <input type="text" name="txtState" value="" /><br />
Zip Code: <input type="text" name="txtZipCode" value="" /><br />
Phone: <input type="text" name="txtPhone" value="" /><br />
E-mail: <input type="text" name="txtEmail" value="" /></p>
<p><input type="submit" value="Save Customer Info" /></p>
</form>
<div id="divStatus"></div>
通過以這種方式返回flase,可以阻止表單的默認行為(將自己提交到服務(wù)器)。通過調(diào)用checkIFrame()方法來啟動隱藏iframe中表單的提交進程。當(dāng)這一任務(wù)完成后,就可以像使用隱藏幀POST請求的例子一樣使用本例;頁面SavaCustomer.php負責(zé)處理數(shù)據(jù),并當(dāng)完成時調(diào)用主頁面中的savaResult()函數(shù)。注意,本節(jié)中的例子是為了使其聚焦于與Ajax技術(shù)相關(guān)的問題上,因而進行了簡化。如果在實際的Web應(yīng)用程序中使用,還需要提供更多的用戶反饋,諸如在發(fā)出請求時屏蔽該表單的輸入等。
6. 隱藏幀技術(shù)的優(yōu)點和缺點
現(xiàn)在,你已經(jīng)對使用隱藏幀所實現(xiàn)的強大功能有所了解了,我們將討論它的實用性。正如前面所說的,該技術(shù)已經(jīng)存在多年,并且仍然在許多Ajax應(yīng)用中使用。使用隱藏幀的一個最大理由之一是它可以維護瀏覽器的歷史,使用戶仍然能夠使用瀏覽器上的后退和前進按鈕。瀏覽器由于并不知道隱藏幀實際上被隱藏了,但對于其所發(fā)出的請求仍然是記錄在案的。然而,Ajax應(yīng)用程序的主頁面卻沒有修改,在隱藏幀中的修改意味著后退和前進按鈕將依據(jù)該隱藏幀的訪問歷史而非主頁面而變化。這也是為什么Gmail和Google Maps仍然使用該技術(shù)的理由。
注意,iframe并非一直會存儲瀏覽器的歷史記錄。盡管IE始終會存儲iframe的歷史記錄,但Firefox只對使用HTML定義(也就是不包括使用JavaScript動態(tài)創(chuàng)建)的iframe保存歷史記錄。Safari從不為iframe保存歷史記錄,不管它們是如何包含在該頁面中的。隱藏幀技術(shù)不利的一面是,對其背后發(fā)生的事了解甚少。它完全依賴于返回的正確頁面。本節(jié)的例子都存在相同的問題:如果隱藏幀的頁面載入失敗,并不會向用戶提示出錯消息;主頁面將繼續(xù)等待直到調(diào)用適當(dāng)?shù)腏avaScript函數(shù)。必須通過設(shè)置一個較長周期(可能是5分鐘)的超時時間,然后如果頁面仍然沒有成功載入則顯示一條消息,以給用戶一個安慰。但這一切都只是一個變通方法,最主要的問題是,對于后臺發(fā)生的HTTP請求缺乏充足的信息。幸運的,我們還有其他選擇。
分享:淺談Ajax的缺點在某網(wǎng)站瞎逛時,發(fā)現(xiàn)這個鏈接,進去逛了逛,覺得很有意思,大家也可以去看看,姑且不論你是否同意里面的觀點,看看別人看待ajax的角度,這對于理解一個事物很有幫助。 下面是我對一部分缺陷的看法: 為Ajax而Ajax(Using Ajax for the sake of Ajax.) 很同意
- Ajax中瀏覽器的緩存問題解決方法
- AJAX和WebService實現(xiàn)省市縣三級聯(lián)動具體代碼
- ajax 登錄功能簡單實現(xiàn)(未連接數(shù)據(jù)庫)
- AJAX和WebService實現(xiàn)郵箱驗證(無刷新驗證郵件地址是否合法)
- AJAX和三層架構(gòu)實現(xiàn)分頁功能具體思路及代碼
- 使用AJAX返回WebService里的集合具體實現(xiàn)
- AJAX獲取服務(wù)器當(dāng)前時間及時間格式輸出處理
- ajax傳遞多個參數(shù)具體實現(xiàn)
- ajax傳遞一個參數(shù)具體實現(xiàn)
- 滑輪滾動到頁面底部ajax加載數(shù)據(jù)配合jsonp實現(xiàn)探討
- jQery ajax——load()方法示例介紹
- jQuery+Ajax實現(xiàn)表格數(shù)據(jù)不同列標(biāo)題排序(為表格注入活力)
- 相關(guān)鏈接:
- 教程說明:
AJAX教程-如何掌握AJAX之AJAX通訊技術(shù)
。