標籤:type div inner mit 檢查 建立 file title nbsp
前面的話
現代Web應用中頻繁使用的一項功能就是表單資料的序列化,XMLHttpRequest 2級為此定義了FormData類型。FormData為序列化表單以及建立與表單格式相同的資料提供了便利。本文將先介紹表單編碼,然後過渡到表單序列化,最後引出FormData的核心概念
表單編碼
當使用者提交表單時,表單中的資料(每個表單元素的名字和值)編碼到一個字串中並隨請求發送。預設情況下,HTML表單通過POST方法發送給伺服器,而編碼後的表單資料則用做請求主體
對錶單資料使用的編碼方案相對簡單:對每個表單元素的名字和值執行普通的URL編碼(使用十六進位轉義碼替換特殊字元),使用等號把編碼後的名字和值分開,並使用"&"符號分開名/值對。一個簡單表單的編碼如下所示
find=pizza&zipcode=01234&radius=1km
表單資料編碼格式有一個正式的MIME類型
application/x-www-form-urlencoded
當使用POST方法提交這種順序的表單資料時,必須"Content-Type"要求標頭為這個值
[注意]這種類型的編碼並不需要HTML表單,在Ajax應用中,希望發送給伺服器的很可能是一個javascript對象
前面展示的資料變成javascript對象的表單編碼形式可能是:
{find: "pizza", zipcode: 01234, radius: "1km"}
表單編碼在Web上如此廣泛使用,同時所有伺服器端的程式設計語言都能得到良好的支援,所以非表單資料的表單編碼通常也是容易實現的事情
下面代碼展示了如何?對象屬性的表單編碼
function encodeFormData(data){ if (!data) return ""; //一直返回字串 var pairs = []; //用於儲存名值對 for(var name in data){ if (!data.hasOwnProperty(name)){ continue; //跳過繼承屬性 } if (typeof data[name] === "function"){ continue; //跳過方法 } var value = data[name].toString(); //把值轉換成字串 name = encodeURIComponent(name.replace("%20", "+")); //編碼名字 value = encodeURIComponent(value.replace("%20", "+"));//編碼值 pairs.push(name + "=" + value); // 存入名值對 } return pairs.join(‘&‘); //返回使用‘&‘串連的名值對}
var data = {name:‘小火柴‘,age:28,sender:‘male‘};//name=%E5%B0%8F%E7%81%AB%E6%9F%B4&age=28&sender=male console.log(encodeFormData(data));
表單序列化
隨著Ajax的出現,表單序列化已經成為一種常見需求。在javascript中,可以利用表單欄位的type屬性,連同name和value屬性一起實現對錶單的序列化。在編寫代碼之前,有必須先搞清楚在表單提交期間,瀏覽器是怎樣將資料發送給伺服器的
1、對錶單欄位的名稱和值進行URL編碼,使用和號(&)分隔
2、不發送禁用的表單欄位
3、只發送勾選的複選框和選項按鈕
4、不發送type為"reset"和"button"的按鈕
5、多選選擇框中的每個選中的值単獨一個條目
6、在單擊提交按鈕提交表單的情況下,也會發送提交按鈕;否則,不發送提交按鈕。也包括type為"image"的<input>元素
7、<select>元素的值,就是選中的<option>元素的value特性的值。如果<option>元素沒有value特性,則是<option>元素的文本值
在表單序列化過程中,一般不包含任何按鈕欄位,因為結果字串很可能是通過其他方式提交的。除此之外的其他上述規則都應該遵循
在下面表單序列化serialize()函數中,首先定義了一個名為parts的數組,用於儲存將要建立的字串的各個部分。然後,通過for迴圈迭代每個表單欄位,並將其儲存在field變數中。在獲得了一個欄位的引用之後,使用switch語句檢測其type屬性
序列化過程中最麻煩的就是<select>元素,它可能是單選框也可能是多選框。為此,需要遍曆控制項中的每一個選項,並在相應選項被選中的情況下向數組中添加一個值。對於單選框,只可能有一個選中項,而多選框則可能有零或多個選中項。這裡的代碼適用於這兩種選擇框,至於可選項的數量則是由瀏覽器控制的。在找到一個選中項之後,需要確定使用什麼值。如果不存在value特性,或者雖然存在該特性,但值為空白字串,都要使用選項的文本來代替。為檢査這個特性,在DOM相容的瀏覽器中需要使用hasAttribute()方法,而在IE7-中需要使用特性的specified屬性
如果表單中包含<fieldset>元素,則該元素會出現在元素集合中,但沒有type屬性。因此,如果type屬性未定義,則不需要對其進行序列化。同樣,對於各種按鈕以及檔案輸入欄位也是如此(檔案輸入欄位在表單提交過程中包含檔案的內容;但是,這個欄位是無法模仿的,序列化時一般都要忽略)
對於選項按鈕和複選框,要檢查其checked屬性是否被設定為false,如果是則退出switch語句。如果checked屬性為true,則繼續執行default語句,即將當前欄位的名稱和值進行編碼,然後添加到parts數組中。函數的最後一步,就是使用join()格式化整個字串,也就是用和號來分隔每一個表單欄位
最後,serialize()函數會以査詢字串的格式輸出序列化之後的字串
function serialize(form){ var parts = [],field = null,option,optValue; for (var i=0, len=form.elements.length; i < len; i++){ field = form.elements[i]; switch(field.type){ //單選或多選的<select>控制項 case "select-one": case "select-multiple": //如果該<select>控制項設定為name屬性 if (field.name.length){ for (var j=0,optLen = field.options.length; j < optLen; j++){ //選擇<option>控制項 option = field.options[j]; //如果該<option>控制項被選中 if (option.selected){ optValue = ""; //測試<option>控制項的value屬性,如果沒有設定,則讀取其text屬性 //IE7-瀏覽器不支援hasAttribute() if (option.hasAttribute){ optValue = (option.hasAttribute("value") ? option.value : option.text); } else { optValue = (option.attributes["value"].specified ? option.value : option.text); } //將鍵和值分別進行編碼並用‘=‘串連起來 parts.push(encodeURIComponent(field.name) + "=" + encodeURIComponent(optValue)); } } } break; case undefined: //fieldset case "file": //file input case "submit": //submit button case "reset": //reset button case "button": //custom button break; case "radio": //radio button case "checkbox": //checkbox //如果沒有選中項 if (!field.checked){ break; } default: if (field.name.length){ parts.push(encodeURIComponent(field.name) + "=" + encodeURIComponent(field.value)); } } } return parts.join("&");}
<form name="form" action="test.php"> <input type="text" value="123" name="text"> <select name="color" multiple> <option>紅</option> <option>綠</option> <option>藍</option> </select> <input type="radio" name="gender" id="male" value="male"><label for="male">男</label> <input type="radio" name="gender" id="female" value="female"><label for="female">女</label> <input type="checkbox" name="time" value="12" id="t12"><label for="t12">12小時</label> <input type="checkbox" name="time" value="24" id="t24"><label for="t24">24小時</label> <button name="btn" type="button">按鈕</button></form><div id="result"></div><script>var oForm = document.forms.form;oForm.onchange = function(e){ e = e || event; result.innerHTML = serialize(form);}</script>
FormData
當HTML表單同時包含檔案上傳元素和其他元素時,瀏覽器不能使用普通的表單編碼而必須使用稱為“multipart/form-data”的特殊Content-Type來用POST方法提交表單。這種編碼包括使用長“邊界”字串把請求主體分離成多個部分。對於文本資料,手動建立“multipart/form-data”請求主體是可能的,但很複雜
XMLHttpRequest 2級為此定義了FormData類型。FormData為序列化表單以及建立與表單格式相同的資料(用於通過XHR傳輸)提供了便利
[注意]IE9-瀏覽器不支援
【建構函式】
new FormData (form? : HTMLFormElement)
選擇性參數form表示一個HTML表單元素,可以包含任何形式的表單控制項,包括檔案輸入框
【append()】
append()方法用於給當前FormData對象添加一個鍵/值對
void append(DOMString name, Blob value, optional DOMString filename);void append(DOMString name, DOMString value);
參數值name表示欄位名稱;參數值value表示欄位值;參數值filename(可選)用於指定檔案的檔案名稱,當value參數被指定為一個Blob對象或者一個File對象時,該檔案名稱會被發送到伺服器上,對於Blob對象來說,這個值預設為"blob"
【其他不常用方法】
get():通過get(key)/getAll(key)來擷取對應的value
set():通過set(key,value)修改資料,如果指定的key不存在則新增一條,如果存在,則修改對應的value值
has():通過has(key)來判斷是否對應的key值
delete():通過delete(key)來刪除資料
[注意]以上4個不常用方法,IE瀏覽器都不支援
var oData1 = new FormData();console.log(oData1.has(‘a‘));//falseoData1.append(‘a‘,1);console.log(oData1.has(‘a‘));//trueconsole.log(oData1.get(‘a‘));//1oData1.set(‘a‘,2);oData1.append(‘a‘,1);console.log(oData1.get(‘a‘));//2console.log(oData1.getAll(‘a‘));//["2", "1"]oData1.delete(‘a‘);console.log(oData1.get(‘a‘));//null
<form action="#" name="form1"> <input type="text" value="1" name="a"></form><script>var oData1 = new FormData(document.forms.form1);console.log(oData1.has(‘a‘));//trueconsole.log(oData1.get(‘a‘));//1oData1.append(‘a‘,2);console.log(oData1.get(‘a‘));//1console.log(oData1.getAll(‘a‘));//[‘1‘,‘2‘]</script>
一般地,我們使用FormData()建構函式建立FormData對象,然後按需多次調用這個對象的append()方法把個體“部分”(可以是字串、File或Blob對象)添加到請求中。最後,把FormData對象傳遞給send()方法,通過XHR對象將其發送到伺服器
[注意]multipart/form-data只能用於post方式
<form action="#" name="form1"><select name="a"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option></select></form><div id="result"></div><script>var oForm = document.forms.form1;oForm.onchange = function(){ //建立xhr對象 var xhr; if(window.XMLHttpRequest){ xhr = new XMLHttpRequest(); }else{ xhr = new ActiveXObject(‘Microsoft.XMLHTTP‘); } //非同步接受響應 xhr.onreadystatechange = function(){ if(xhr.readyState == 4){ if(xhr.status == 200){ //實際操作 result.innerHTML = xhr.responseText; } } } //發送請求 xhr.open(‘post‘,‘t1.php‘ ,true); xhr.send(new FormData(form1)); }</script>
深入理解ajax系列第四篇