博客 / 詳情

返回

zepto源碼分析之form模塊

前言

JavaScript最初的一個應用場景就是分擔服務器處理表單的責任,打破處處依賴服務器的局面,這篇文章主要介紹zepto中form模塊關於表單處理的幾個方法,serializeserializeArraysubmit

原文鏈接

github項目地址

表單相關回顧

在開始學些form模塊相關方法前,我們先來回顧一下表單提交時,瀏覽器是怎麼樣將數據發送給服務器的(以下內容摘自《JavaScript高級程序設計》第14章 14.4節 表單序列化)

  1. 對錶單字段的名稱和值進行URL編碼,使用&分隔。
  2. 不發送禁用的表單字段。(也就是屬性disabled為true的)
  3. 只發送勾選的複選框和單選按鈕
  4. 不發送type為reset和button的按鈕
  5. 多選選擇框中每個選擇的值單獨一個條目
  6. 在單擊提交按鈕表單的情況下,也會發送提交按鈕的value值,否則不發送提交按鈕。
  7. select元素的值,就是選中的option元素的value屬性的值,如果option元素沒有value屬性,則是option元素的文本值。 在表單序列化得過程中,一般不包含任何按鈕字段,因為結果字符串很可能是通過其他方式提交的,除此之外其他規則都應該遵循。

有了上面的知識的回顧,接下來我們開始看zepto中serializeserializeArray的實現

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),再接着就是判斷符合提交到服務器端的表單控件條件了。

  1. 需要有name屬性(條件為"真")
  2. 不能是fieldset元素
  3. 不能是已經禁止的元素(即disable為true)
  4. 不能是submit、reset、button、file等元素
  5. 對於單選和多選控件,只發送已經勾選的。

在上面的條件都滿足的條件下,調用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模塊

  1. zepto源碼分析之form模塊

zepto模塊

  1. 這些Zepto中實用的方法集
  2. Zepto核心模塊之工具方法拾遺

event模塊

  1. mouseenter與mouseover為何這般糾纏不清?
  2. 向zepto.js學習如何手動觸發DOM事件
  3. 誰説你只是"會用"jQuery?

ajax模塊

  1. 原來你是這樣的jsonp(原理與具體實現細節)
user avatar ziyeliufeng 頭像 dujing_5b7edb9db0b1c 頭像 suporka 頭像 201926 頭像 b_a_r_a_n 頭像 icezero 頭像 beilee 頭像 513928731 頭像 ipromise 頭像 denzel 頭像 taoqun 頭像 wuyuedexingkong 頭像
22 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.