讀Zepto原始碼之Form模組

对角另一面發表於2017-10-23

Form 模組處理的是表單提交。表單提交包含兩部分,一部分是格式化表單資料,另一部分是觸發 submit 事件,提交表單。

讀 Zepto 原始碼系列文章已經放到了github上,歡迎star: reading-zepto

原始碼版本

本文閱讀的原始碼為 zepto1.2.0

GitBook

reading-zepto

.serializeArray()

$.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 是格式化部分的核心方法,後面的 serialize 方法內部呼叫的也是 serializeArray 方法。

serializeArray 最終返回的結果是一個陣列,每個陣列項為包含 namevalue 屬性的物件。其中 name 為表單元素的 name 屬性值。

add函式

add = function(value) {
  if (value.forEach) return value.forEach(add)
  result.push({ name: name, value: value })
}複製程式碼

表單的值交由 add 函式處理,如果值為陣列(支援 forEach ) 方法,則呼叫 forEach 遍歷,繼續由 add 函式處理。否則將結果存入陣列 result 中。最後返回的結果也是這個 result

遍歷表單元素

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())
})複製程式碼

如果集合中有多個表單,則只處理第一個表單的表單元素。this[0].elements 用來獲取第一個表單所有的表單元素。

type 為表單型別,name 為表單元素的 name 屬性值。

這一大段程式碼的關鍵在 if 中的條件判斷,其實是將一些無關的表單元素排除,只處理符合條件的表單元素。

以下一個條件一個條件來分析:

  • field.nodeName.toLowerCase() != 'fieldset' 排除 fieldset 元素;
  • !field.disabled 排除禁用的表單,已經禁用了,肯定是沒有值需要提交的了;
  • type != 'submit' 排除確定按鈕;
  • type != 'reset' 排除重置按鈕;
  • type != 'button' 排除按鈕;
  • type != 'file' 排除檔案選擇控制元件;
  • ((type != 'radio' && type != 'checkbox') || field.checked)) 如果是 radiocheckbox 時,則必須要選中,這個也很好理解,如果沒有選中,也不會有值需要處理。

然後呼叫 add 方法,將表單元素的值獲取到交由其處理。

.serialize()

$.fn.serialize = function(){
  var result = []
  this.serializeArray().forEach(function(elm){
    result.push(encodeURIComponent(elm.name) + '=' + encodeURIComponent(elm.value))
  })
  return result.join('&')
}複製程式碼

表單元素處理完成後,最終是要拼成如 name1=value1&name2=value2&... 的形式,serialize 方法要做的就是這部分事情。

這裡對 serizlizeArray 返回的陣列再做進一步的處理,首先用 encodeURIComponent 序列化 namevalue 的值,並用 = 號拼接成字串,存進新的陣列中,最後呼叫 join 方法,用 & 將各項拼接起來。

.submit()

$.fn.submit = function(callback) {
  if (0 in arguments) this.bind('submit', callback)
  else if (this.length) {
    var event = $.Event('submit')
    this.eq(0).trigger(event)
    if (!event.isDefaultPrevented()) this.get(0).submit()
  }
  return this
}複製程式碼

處理完資料,接下來該到提交了。

if (0 in arguments) this.bind('submit', callback)複製程式碼

如果有傳遞迴調函式 callback ,則在表單上繫結 submit 事件,以 callback 作為事件的回撥。

else if (this.length) {
  var event = $.Event('submit')
  this.eq(0).trigger(event)
  if (!event.isDefaultPrevented()) this.get(0).submit()
}複製程式碼

否則手動繫結 submit 事件,如果沒有阻止瀏覽器的預設事件,則在第一個表單上觸發 submit ,提交表單。

注意 eqget 的區別, eq 返回的是 Zepto 物件,而 get 返回的是 DOM 元素。

系列文章

  1. 讀Zepto原始碼之程式碼結構
  2. 讀Zepto原始碼之內部方法
  3. 讀Zepto原始碼之工具函式
  4. 讀Zepto原始碼之神奇的$
  5. 讀Zepto原始碼之集合操作
  6. 讀Zepto原始碼之集合元素查詢
  7. 讀Zepto原始碼之操作DOM
  8. 讀Zepto原始碼之樣式操作
  9. 讀Zepto原始碼之屬性操作
  10. 讀Zepto原始碼之Event模組
  11. 讀Zepto原始碼之IE模組
  12. 讀Zepto原始碼之Callbacks模組
  13. 讀Zepto原始碼之Deferred模組
  14. 讀Zepto原始碼之Ajax模組
  15. 讀Zepto原始碼之Assets模組
  16. 讀Zepto原始碼之Selector模組
  17. 讀Zepto原始碼之Touch模組
  18. 讀Zepto原始碼之Gesture模組
  19. 讀Zepto原始碼之IOS3模組
  20. 讀Zepto原始碼之Fx模組
  21. 讀Zepto原始碼之fx_methods模組
  22. 讀Zepto原始碼之Stack模組

附文

參考

License

署名-非商業性使用-禁止演繹 4.0 國際 (CC BY-NC-ND 4.0)

最後,所有文章都會同步傳送到微信公眾號上,歡迎關注,歡迎提意見:

作者:對角另一面

相關文章