前言
JavaScript最初的一個應用場景就是分擔伺服器處理表單的責任,打破處處依賴伺服器的局面,這篇文章主要介紹zepto中
form
模組關於表單處理的幾個方法,serialize
、serializeArray
、submit
。
表單相關回顧
在開始學些form模組相關方法前,我們先來回顧一下表單提交時,瀏覽器是怎麼樣將資料傳送給伺服器的(以下內容摘自《JavaScript高階程式設計》第14章 14.4節 表單序列化)
- 對錶單欄位的名稱和值進行URL編碼,使用&分隔。
- 不傳送禁用的表單欄位。(也就是屬性disabled為true的)
- 只傳送勾選的核取方塊和單選按鈕
- 不傳送type為reset和button的按鈕
- 多選選擇框中每個選擇的值單獨一個條目
- 在單擊提交按鈕表單的情況下,也會傳送提交按鈕的value值,否則不傳送提交按鈕。
- select元素的值,就是選中的option元素的value屬性的值,如果option元素沒有value屬性,則是option元素的文字值。 在表單序列化得過程中,一般不包含任何按鈕欄位,因為結果字串很可能是通過其他方式提交的,除此之外其他規則都應該遵循。
有了上面的知識的回顧,接下來我們開始看zepto中serialize
和serializeArray
的實現
serializeArray
因為serialize依賴serializeArray的實現,所以我們先來看看它是怎麼實現的。而他的作用是把form表單序列化成一個由 name 和 value 屬性組成的物件的陣列。形如:
[
{name: 'qianlongo', value: 'haha'},
{name: 'wangmin', value: 'heihei'}
]複製程式碼
原始碼
$.fn.serializeArray = function() {
var name, type, result = [],
add = function(value) {
if (value.forEach) return value.forEach(add)
result.push({ name: name, value: value })
}
if (this[0]) $.each(this[0].elements, function(_, field){
type = field.type, name = field.name
if (name && field.nodeName.toLowerCase() != 'fieldset' &&
!field.disabled && type != 'submit' && type != 'reset' && type != 'button' && type != 'file' &&
((type != 'radio' && type != 'checkbox') || field.checked))
add($(field).val())
})
return result
}複製程式碼
在$的原型上新增了serializeArray
相關方法。一開始宣告瞭name
,type
, result
三個變數,分別儲存表單控制元件的name屬性,type屬性,以及最後函式執行完成後要返回的陣列。
首先通過this[0]
判斷有未選中表單元素,如果沒有返回的結果就是一個空陣列了。如果選中了,則對該表單的相關控制元件(form.elements
表示表單中所有控制元件的集合)進行遍歷。
獲取單個控制元件的型別(type),name屬性(name),再接著就是判斷符合提交到伺服器端的表單控制元件條件了。
- 需要有name屬性(條件為"真")
- 不能是
fieldset
元素 - 不能是已經禁止的元素(即disable為true)
- 不能是submit、reset、button、file等元素
- 對於單選和多選控制元件,只傳送已經勾選的。
在上面的條件都滿足的條件下,呼叫add
函式並將通過$(elements).val()獲取到的值傳入。
add函式的邏輯也非常簡單。如果value是陣列,則將value陣列遞迴的每一項傳入add。不是陣列就是直接按照{ name: name, value: value }
形式推入result了。
不過什麼時候value會為陣列呢?我們需要從zepto模組的val函式實現看起
val函式實現
function val (value) {
if (0 in arguments) {
if (value == null) value = ""
return this.each(function (idx) {
this.value = funcArg(this, value, idx, this.value)
})
} else {
// 主要看這裡,multiple是用來設定下拉選單是否可以多選的。
// 如果是多選的,則選擇被選中(即selected為true)的元素並通過pluck方法,讀取該元素的value值,最後返回的是一個陣列
return this[0] && (this[0].multiple ?
$(this[0]).find('option').filter(function () { return this.selected }).pluck('value') :
this[0].value)
}
}複製程式碼
serialize
將表單內容序列化為查詢字串。類似
name=qianlongo&sex=boy
原始碼
$.fn.serialize = function(){
var result = []
this.serializeArray().forEach(function(elm){
// 每個表單的name和value都通過encodeURIComponent編碼
result.push(encodeURIComponent(elm.name) + '=' + encodeURIComponent(elm.value))
})
// 最後通過&符號分割
return result.join('&')
}複製程式碼
有了serializeArray
的基礎,serialize
就是將相應的name和value都通過encodeURIComponent
編碼,然後用&
符號進行分割,也就達到了我們要的結果。
submit
有兩種用法,當傳入了一個回撥函式的時候,是給指定的表單的
submit
事件新增一個回撥處理函式。如果沒有傳入回撥函式則觸發當前表單
submit
事件,並且執行預設的提交表單行為(前提是沒有阻止瀏覽器預設行為)
原始碼
$.fn.submit = function(callback) {
// 如果傳了回撥函式,則在選中的元素上新增submit事件
if (0 in arguments) this.bind('submit', callback)
// 否則在沒有傳遞迴調函式的情況下,並且選中有表單元素
else if (this.length) {
var event = $.Event('submit')
// 觸發選中的第一個表單的是submit事件,注意這裡只是手動觸發繫結的submit事件,並不會提交表單
this.eq(0).trigger(event)
// 如果沒有阻止預設事件,便呼叫form.submit()提交表單
if (!event.isDefaultPrevented()) this.get(0).submit()
}
return this
}複製程式碼
結尾
以上是zepto form模組的相關原始碼分析,歡迎大家指正。
文章記錄
form模組
zepto模組
event模組
ajax模組