Zepto源碼之$.fn工具函數

$.fn.get

從當前對象集合中獲取所有元素或單個元素。當index參數不存在的時,以普通數組的方式返回所有的元素。當指定index時,只返回該置的元素。這點與eq不同,該方法返回的是DOM節點,不是Zepto對象集合。

get: function(idx){
      return idx === undefined ? slice.call(this) : this[idx >= 0 ? idx : idx + this.length]
    },

// 這裏用的是三目運算符,我們可以簡單的改寫一下
get: function(idx) {
    if(idx === undefined) {
    // 沒有設置 idx 返回全部的元素 
        return slice.call(this)
        // this.slice()
    } else {
    // 設置了 idx 的情況,取對應的元素
        if(idx >= 0) {
            // idx 大於等於0 從頭開始取
            return this[idx]
        } else {
             // idx 小於0 從尾開始取
            return this[idx + this.length]
        }
    }
}

$.fn.toArray

這是一個彩蛋驚喜,本質是暴露出來的,不過官方的API文檔上並沒有。其主要是內部使用,用於調用 $.fn.get 把傳入的 zepto 對象轉化成一個數組。

toArray: function(){ return this.get() },

$.fn.concat

添加元素到一個Zepto對象集合形成一個新數組。如果參數是一個數組,那麼這個數組中的元素將會合併到Zepto對象集合中。

concat: function(){
      var i, value, args = []
      // 遍歷傳入的參數
      for (i = 0; i < arguments.length; i++) {
        value = arguments[i]
        // 檢測元素是不是 zepto 對象,如果是,那麼轉化成數組插入到 臨時數組中,如果是元素,那麼直接插入到臨時數組中 
        args[i] = zepto.isZ(value) ? value.toArray() : value
      }
      // 檢測當前調用 concat函數的對象是不是 zepto 對象,如果是那麼先轉化成數組再與args拼接,否則直接與args拼接。返回拼接結果
      return concat.apply(zepto.isZ(this) ? this.toArray() : this, args)
    },

map

遍歷對象集合中的所有元素。通過遍歷函數返回值形成一個新的集合對象。

map: function(fn){
      // 先調用 $.map 方法,把對象中的數據全用 fn 處理一遍,生成一個數組。再將數組中的內容通過 $() 方法轉化成 zepto 對象。
      return $($.map(this, function(el, i){ return fn.call(el, i, el) }))
    },

slice

提取這個數組array的子集,從start開始,如果給定end,提取從從start開始到end結束的元素,但是不包含end位置的元素。

slice: function(){
      // 調用 slice 方法,截取該數組,並轉化成 zepto 對象 
      return $(slice.apply(this, arguments))
    },

ready

ready: function(callback){
      // 通過檢測 document.readyState 來判斷頁面是否加載成功,  
      // doScroll 是用於檢測 IE 瀏覽器加載狀態的
      // 如果頁面加載成功,那麼立即將回調函數放在執行隊形中
      if (document.readyState === "complete" ||
          (document.readyState !== "loading" && !document.documentElement.doScroll))
        setTimeout(function(){ callback($) }, 0)
      else {
        // 添加事件監聽,如果監聽到 DOMContented 或者 load 事件,那麼就表明頁面執行完成,執行 handler
        var handler = function() {
          document.removeEventListener("DOMContentLoaded", handler, false)
          window.removeEventListener("load", handler, false)
          callback($)
        }
        document.addEventListener("DOMContentLoaded", handler, false)
        window.addEventListener("load", handler, false)
      }
      return this
    },

size

獲取對象集合中元素的數量。

size: function(){
      return this.length
    },

each

遍歷一個對象集合每個元素。如果迭代函數返回 false,遍歷結束。

// emptyArray 的定義在頂部數據定義部分
emptyArray = []

each: function(callback){
      // 使用 Array.prototype.every 然後監測返回值,就可以做到當迭代函數返回 false 時,遍歷結束
      emptyArray.every.call(this, function(el, idx){
        return callback.call(el, idx, el) !== false
      })
      return this
    },

remove

從其父節點中刪除當前集合中的元素,有效的從dom中移除。

remove: function(){
      // 本函數本質上是返回 this 。即當前操作的 zepto 對象。這樣做的好處是可以鏈式調用
      return this.each(function(){
      // 遍歷當前操作的 zepto 對象。如果其存在父元素,那麼就將其從父元素中移除
        if (this.parentNode != null)
          this.parentNode.removeChild(this)
      })
    },

not

過濾當前對象集合,獲取一個新的對象集合,它裏面的元素不能匹配css選擇器。如果另一個參數爲Zepto對象集合,那麼返回的新Zepto對象中的元素都不包含在該參數對象中。如果參數是一個函數。僅僅包含函數執行爲false值得時候的元素,函數的 this 關鍵字指向當前循環元素。

not: function(selector){
      var nodes=[]
      // 如果傳入內容是一個函數,且函數存在call方法
      if (isFunction(selector) && selector.call !== undefined)
        // 對當前指向的DOM數組執行 each 方法,如果返回值爲false,傳到到nodes數組中        
        this.each(function(idx){
          if (!selector.call(this,idx)) nodes.push(this)
        })
      else {
        // 當 selector 爲字符串時,對數組進行篩選,找出滿足selector的內容
        // 當 selector 爲 nodeList 時執行 slice.call(selector) ,這裏的isFunction(selector.item)是爲了排除selector爲數組的情況
        //當selector爲css選擇器,執行$(selector)
        var excludes = typeof selector == 'string' ? this.filter(selector) :
          (likeArray(selector) && isFunction(selector.item)) ? slice.call(selector) : $(selector)
        //篩選出不在 excludes 數組中的記錄,達到除去的目的
        this.forEach(function(el){
          if (excludes.indexOf(el) < 0) nodes.push(el)
        })
      }
      // 將nodes數組轉化成 zepto 對象並返回
      return $(nodes)
    },

filter

過濾對象集合,返回對象集合中滿足css選擇器的項。如果參數爲一個函數,函數返回有實際值得時候,元素纔會被返回。

filter: function(selector){
      // 如果傳入內容是個函數,那麼返回 兩次not函數篩選的結果,負負爲正
      if (isFunction(selector)) return this.not(this.not(selector))
      // 如果傳入的是一個css選擇器,那麼執行 matches 來篩選,並將結果轉化成一個 zepto 數組
      return $(filter.call(this, function(element){
        return zepto.matches(element, selector)
      }))
    },

關於 matches 函數,我在上面文章 Zepto源碼二之工具函數中提到過,在此略過

add

添加元素到當前匹配的元素集合中。如果給定content參數,將只在content元素中進行查找,否則在整個document中查找。

add: function(selector,context){
// 先調用 $ 函數,將 selector轉化成zepto對象,再拼接到現在操作的 zepto 對象上,再去重,將去重結果再轉化成zepto對象並返回
    return $(uniq(this.concat($(selector,context))))
    },

is

判斷當前元素集合中的第一個元素是否符css選擇器。對於基礎支持jquery的非標準選擇器類似: :visible包含在可選的“selector”模塊中。

is: function(selector){
      // 先判斷傳入的是不是css選擇器(通常是string形字符串)
      // 如果當前操作的 zepto 對象存在,且有內容,那麼用 matches 匹配傳入的內容是不是第一個,如果是 返回 true,否則返回 false
      return typeof selector == 'string' ? this.length > 0 && zepto.matches(this[0], selector) : 
      // 如果不存在則尷尬了,只能強行檢驗傳入的selector與當前對象的selector是不是相等,返回比較結果
          selector && this.selector == selector.selector
    },

find

在當對象前集合內查找符合CSS選擇器的每個元素的後代元素。如果給定Zepto對象集合或者元素,過濾它們,只有當它們在當前Zepto集合對象中時,纔回被返回。

find: function(selector){
      var result, $this = this
      // 如果沒有傳入選擇器,返回一個包含空值 zepto 對象
      if (!selector) result = $()
      else if (typeof selector == 'object')
      // 如果選擇器是一個對象
        result = $(selector).filter(function(){
          var node = this
          // //如果 $.contains 返回 true ,則 emptyArray.some 也會返回true,外層的filter則會收錄該條記錄
          return emptyArray.some.call($this, function(parent){
            return $.contains(parent, node)
          })
        })
        // 如果selector是css選擇器
        // 如果當前集合長度爲1時,調用zepto.qsa,將結果轉成zepto對象
      else if (this.length == 1) result = $(zepto.qsa(this[0], selector))
        // 如果長度大於1,則調用map遍歷
      else result = this.map(function(){ return zepto.qsa(this, selector) })
      // 返回結果
      return result
    },

has

判斷當前對象集合的子元素是否有符合選擇器的元素,或者是否包含指定的DOM節點,如果有,則返回新的對象集合,該對象過濾掉不含有選擇器匹配元素或者不含有指定DOM節點的對象。

has: function(selector){
      return this.filter(function(){
        return isObject(selector) ?
          $.contains(this, selector) :
          $(this).find(selector).size()
      })
    },

// 這一段可以這麼改寫以便於理解
changeHas: funcion(selector){
    var nodes
    // 如果傳入內容是一個對象,那麼直接使用 $.contains 來檢測,否則用 find 來查找
    if(isObject(selector)){
        nodes = $.contains(this, selector)
    } else {
        nodes = $(this).find(selector).size()
    }
    // 返回查找結果用 filter 篩選後的結果
    return this.filter(nodes)
}

eq

從當前對象集合中獲取給定索引值的元素。

eq: function(idx){
    // + idx +1 是隱式類型轉換,確定idx是一個數字
      return idx === -1 ? this.slice(idx) : this.slice(idx, + idx + 1)
    },

first

獲取當前對象集合中的第一個元素。

first: function(){
      var el = this[0]
      // 如果 el 存在,那麼將它轉化成 zepto 對象傳回
      return el && !isObject(el) ? el : $(el)
    },

last

獲取當前對象集合中的最後一個元素。

last: function(){
      var el = this[this.length - 1]
      return el && !isObject(el) ? el : $(el)
    },

closest

從元素本身開始,逐級向上級元素匹配,並返回最先匹配selector的元素。如果給定context節點參數,那麼只匹配該節點的後代元素。這個方法與 parents(selector)有點相像,但它只返回最先匹配的祖先元素。
如果參數是一個Zepto對象集合或者一個元素,結果必須匹配給定的元素而不是選擇器。

closest: function(selector, context){
      // 判斷 selector 是不是一個對象如果是那麼轉化成一個 zepto 對象並賦值給 collection ,如果不是那麼將 collection 賦值爲 false
      var nodes = [], collection = typeof selector == 'object' && $(selector)

      this.each(function(_, node){
      // 當selector是字符串選擇器時,如果node與selector不匹配,則需要取node.parentNode進行判斷
      // 當node 不是context,document的時候,取node.parentNode
        while (node && !(collection ? collection.indexOf(node) >= 0 : zepto.matches(node, selector)))
          node = node !== context && !isDocument(node) && node.parentNode
        if (node && nodes.indexOf(node) < 0) nodes.push(node)
      })
      // 將結果轉換成一個 zepto 對象
      return $(nodes)
    },

parents

獲取對象集合每個元素所有的祖先元素。如果css選擇器參數給出,過濾出符合條件的元素。

parents: function(selector){
      var ancestors = [], nodes = this
      while (nodes.length > 0)
        nodes = $.map(nodes, function(node){
          // 當selector是字符串選擇器時,如果node與selector不匹配,則需要取node.parentNode進行判斷
          // 當node 不是context,document的時候,取node.parentNode
          if ((node = node.parentNode) && !isDocument(node) && ancestors.indexOf(node) < 0) {
            ancestors.push(node)
            return node
          }
        })
      return filtered(ancestors, selector)
    },

pluck

獲取對象集合中每一個元素的屬性值。返回值爲 null或undefined值得過濾掉。

pluck: function(property){
      return $.map(this, function(el){ return el[property] })
    },

parent

獲取對象集合中每個元素的直接父元素。如果css選擇器參數給出。過濾出符合條件的元素。

parent: function(selector){
      // 找出直接父元素,再過濾重複值篩選一下,返回結果
      return filtered(uniq(this.pluck('parentNode')), selector)
    },

children

獲得每個匹配元素集合元素的直接子元素,如果給定selector,那麼返回的結果中只包含符合css選擇器的元素。


// 得到element元素的子元素(nodeType === 1)集合
// 如果element支持children屬性則直接返回
// 反則遍歷子節點中nodeType爲1的節點(即元素節點)

children: function(selector){
      return filtered(this.map(function(){ return children(this) }), selector)
    },

contents

獲得每個匹配元素集合元素的子元素,包括文字和註釋節點。

contents: function() {
      return this.map(function() { return this.contentDocument || slice.call(this.childNodes) })
    },

siblings

獲取對象集合中所有元素的兄弟節點。如果給定CSS選擇器參數,過濾出符合選擇器的元素。


// 先獲取當前元素的父節點,然後獲取該父節點的所有子節點
// 再從所有子節點中去掉當前元素
// 最後如果傳了selector,再將所有的子節點過濾出符合selector條件的
siblings: function(selector){
      return filtered(this.map(function(i, el){
        return filter.call(children(el.parentNode), function(child){ return child!==el })
      }), selector)
    },

empty

清空對象集合中每個元素的DOM內容。

empty: function(){
      return this.each(function(){ this.innerHTML = '' })
    },

show

恢復對象集合中每個元素默認的“display”值。如果你用 hide將元素隱藏,用該屬性可以將其顯示。相當於去掉了display:none。

show: function(){
      return this.each(function(){
        this.style.display == "none" && (this.style.display = '')
        // 通過 getComputedStyle 來查看當前元素計算後的樣式,如果 display 爲 none 。那麼將其 display 設置成默認
        if (getComputedStyle(this, '').getPropertyValue("display") == "none")
          this.style.display = defaultDisplay(this.nodeName)
      })
    },

replaceWith

用給定的內容替換所有匹配的元素。(包含元素本身)。content參數可以爲 before中描述的類型。

replaceWith: function(newContent){
      // 在該節點前面插入新的內容,然後刪除該節點,以達到替換的目的
      return this.before(newContent).remove()
    },

wrap

在每個匹配的元素外層包上一個html元素。structure參數可以是一個單獨的元素或者一些嵌套的元素。也可以是一個html字符串片段或者dom節點。還可以是一個生成用來包元素的回調函數,這個函數返回前兩種類型的包裹片段。

wrap: function(structure){
      var func = isFunction(structure)
      // 當前操作的 zepto 對象不爲空且 structure 不爲函數
      if (this[0] && !func)
      // 將 structure 的第一個元素賦值給 dom 
      // 如果dom元素的parentNode存在或者當前選中的元素個人大於1那麼clone爲true
        var dom   = $(structure).get(0),
            clone = dom.parentNode || this.length > 1

      return this.each(function(index){
        // 如果structure爲函數,則將當前的元素和對應的索引傳入函數
        $(this).wrapAll(
          func ? structure.call(this, index) :
            clone ? dom.cloneNode(true) : dom
        )
      })
    },

wrapAll

在所有匹配元素外面包一個單獨的結構。結構可以是單個元素或 幾個嵌套的元素,並且可以通過在作爲HTML字符串或DOM節點。

wrapAll: function(structure){
      if (this[0]) {
      // 如果當前操作的 zepto 對象存在
      // 在當前操作的 zepto 對象前面插入被轉化成 zepto 對象的 structure 對象
        $(this[0]).before(structure = $(structure))
        var children
        // 獲取structure的最深層次的第一個子元素
        // 將當前的元素集合通過append方法添加到structure末尾
        while ((children = structure.children()).length) structure = children.first()
        $(structure).append(this)
      }
      return this
    },

wrapInner

將每個元素中的內容包裹在一個單獨的結構中。結構可以是單個元件或多個嵌套元件,並且可以通過在作爲HTML字符串或DOM節點,或者是一個生成用來包元素的回調函數,這個函數返回前兩種類型的包裹片段。

wrapInner: function(structure){
      var func = isFunction(structure)
      return this.each(function(index){
        var self = $(this), contents = self.contents(),
        // contents => 獲取當前元素的所有子節點(包括元素節點和文本節點)
        // structure爲函數則將其執行結果賦值爲dom,否則直接將其賦值
        // 當前元素的子節點不爲空,則調用wrapAll,否則直接將dom插入self當前元素即可
            dom  = func ? structure.call(this, index) : structure
        contents.length ? contents.wrapAll(dom) : self.append(dom)
      })
    },

unwrap

移除集合中每個元素的直接父節點,並把他們的子元素保留在原來的位置。 基本上,這種方法刪除上一的祖先元素,同時保持DOM中的當前元素。


unwrap: function(){
      // 通過parent()獲取當前元素集合的所有直接父節點
      // 將獲取到的父節點集合進行遍歷
      // 將該父節點替換爲該父節點的所有子節點
      this.parent().each(function(){
        $(this).replaceWith($(this).children())
      })
      return this
    },

clone

通過深度克隆來複制集合中的所有元素。


// 將當前元素賦值一份,用了cloneNode這個原生方法,並且傳了true

clone: function(){
      return this.map(function(){ return this.cloneNode(true) })
    },

hide

通過設置css的屬性display 爲 none來將對象集合中的元素隱藏。

hide: function(){
      return this.css("display", "none")
    },

toggle

顯示或隱藏匹配元素。如果 setting爲true,相當於show 法。如果setting爲false。相當於 hide方法。

toggle: function(setting){
      return this.each(function(){
        var el = $(this)
        // 先判斷有沒有傳入 setting 如果設置了,則使用 setting 的規則來進行設置
        // 如果沒有設置 setting ,那麼通過元素的 display 判斷來進行設置顯示或者隱藏
        ;(setting === undefined ? el.css("display") == "none" : setting) ? el.show() : el.hide()
      })
    },

prev

獲取對象集合中每一個元素的前一個兄弟節點,通過選擇器來進行過濾。

prev: function(selector){ return $(this.pluck('previousElementSibling')).filter(selector || '*') },

next

獲取對象集合中每一個元素的下一個兄弟節點(可以選擇性的帶上過濾選擇器)。

next: function(selector){ return $(this.pluck('nextElementSibling')).filter(selector || '*') },

html

獲取或設置對象集合中元素的HTML內容。當沒有給定content參數時,返回對象集合中第一個元素的innerHtml。當給定content參數時,用其替換對象集合中每個元素的內容。content可以是append中描述的所有類型。


html: function(html){
      return 0 in arguments ?
        this.each(function(idx){
          var originHtml = this.innerHTML
          $(this).empty().append( funcArg(this, html, idx, originHtml) )
        }) :
        (0 in this ? this[0].innerHTML : null)
    },

// 簡單的改寫一下

html: function(html) {
    // 沒有傳入參數,那麼直接獲取第一個元素的html內容
    if(html === undefined){
        if(this[0]){
        // 第一個元素存在就返回內容
            return this[0].innerHTML
        } else {
        // 不存在就返回 null
            return null
        }
    } else {
        return this.each(function(idx){
          var originHtml = this.innerHTML
          $(this).empty().append( funcArg(this, html, idx, originHtml) )
        })
    }
}

text

獲取或者設置所有對象集合中元素的文本內容。當沒有給定content參數時,返回當前對象集合中第一個元素的文本內容(包含子節點中的文本內容)。當給定content參數時,使用它替換對象集合中所有元素的文本內容。它有待點似 html,與它不同的是它不能用來獲取或設置 HTML。

text: function(text){
      return 0 in arguments ?
        this.each(function(idx){
          var newText = funcArg(this, text, idx, this.textContent)
          this.textContent = newText == null ? '' : ''+newText
        }) :
        (0 in this ? this.pluck('textContent').join("") : null)
    },

attr

讀取或設置dom的屬性。如果沒有給定value參數,則讀取對象集合中第一個元素的屬性值。當給定了value參數。則設置對象集合中所有元素的該屬性的值。當value參數爲null,那麼這個屬性將被移除(類似removeAttr),多個屬性可以通過對象鍵值對的方式進行設置。

attr: function(name, value){
      var result
      return (typeof name == 'string' && !(1 in arguments)) ?
      // 獲取屬性
        (0 in this && this[0].nodeType == 1 && (result = this[0].getAttribute(name)) != null ? result : undefined) :
        // 設置屬性
        this.each(function(idx){
          if (this.nodeType !== 1) return
          // 設置多個屬性值 
          if (isObject(name)) for (key in name) setAttribute(this, key, name[key])
          // 設置一個屬性值
          else setAttribute(this, name, funcArg(this, value, idx, this.getAttribute(name)))
        })
    },

removeAttr

移除當前對象集合中所有元素的指定屬性。

removeAttr: function(name){
      return this.each(function(){ this.nodeType === 1 && name.split(' ').forEach(function(attribute){
        setAttribute(this, attribute)
      }, this)})
    },

prop

讀取或設置dom元素的屬性值。它在讀取屬性值的情況下優先於 attr,因爲這些屬性值會因爲用戶的交互發生改變,如checked 和 selected。
簡寫或小寫名稱,比如for, class, readonly及類似的屬性,將被映射到實際的屬性上,比如htmlFor, className, readOnly, 等等。

prop: function(name, value){
      name = propMap[name] || name
      return (typeof name == 'string' && !(1 in arguments)) ?
        (this[0] && this[0][name]) :
        this.each(function(idx){
          if (isObject(name)) for (key in name) this[propMap[key] || key] = name[key]
          else this[name] = funcArg(this, value, idx, this[name])
        })
    },

removeProp

從集合的每個DOM節點中刪除一個屬性。這是用JavaScript的delete操作符完成。值得注意的是如果嘗試刪除DOM的一些內置屬性,如className或maxLength,將不會有任何效果,因爲瀏覽器禁止刪除這些屬性。

removeProp: function(name){
      name = propMap[name] || name
      return this.each(function(){ delete this[name] })
    },

data

讀取或寫入dom的 data-* 屬性。行爲有點像 attr ,但是屬性名稱前面加上 data-。

data: function(name, value){
      var attrName = 'data-' + name.replace(capitalRE, '-$1').toLowerCase()

      var data = (1 in arguments) ?
        this.attr(attrName, value) :
        this.attr(attrName)

      return data !== null ? deserializeValue(data) : undefined
    },

val

獲取或設置匹配元素的值。當沒有給定value參數,返回第一個元素的值。如果是標籤,則返回一個數組。當給定value參數,那麼將設置所有元素的值。

val: function(value){
      if (0 in arguments) {
        if (value == null) value = ""
        return this.each(function(idx){
          this.value = funcArg(this, value, idx, this.value)
        })
      } else {
        return this[0] && (this[0].multiple ?
           $(this[0]).find('option').filter(function(){ return this.selected }).pluck('value') :
           this[0].value)
      }
    },

offset

獲得當前元素相對於document的位置。返回一個對象含有: top, left, width和height
當給定一個含有left和top屬性對象時,使用這些值來對集合中每一個元素進行相對於document的定位。

offset: function(coordinates){
      if (coordinates) return this.each(function(index){
        var $this = $(this),
            coords = funcArg(this, coordinates, index, $this.offset()),
            parentOffset = $this.offsetParent().offset(),
            props = {
              top:  coords.top  - parentOffset.top,
              left: coords.left - parentOffset.left
            }

        if ($this.css('position') == 'static') props['position'] = 'relative'
        $this.css(props)
      })
      if (!this.length) return null
      if (document.documentElement !== this[0] && !$.contains(document.documentElement, this[0]))
        return {top: 0, left: 0}
      var obj = this[0].getBoundingClientRect()
      return {
        left: obj.left + window.pageXOffset,
        top: obj.top + window.pageYOffset,
        width: Math.round(obj.width),
        height: Math.round(obj.height)
      }
    },

css

讀取或設置DOM元素的css屬性。當value參數不存在的時候,返回對象集合中第一個元素的css屬性。當value參數存在時,設置對象集合中每一個元素的對應css屬性。
多個屬性可以通過傳遞一個屬性名組成的數組一次性獲取。多個屬性可以利用對象鍵值對的方式進行設置。
當value爲空(空字符串,null 或 undefined),那個css屬性將會被移出。當value參數爲一個無單位的數字,如果該css屬性需要單位,“px”將會自動添加到該屬性上。

css: function(property, value){
      // 如果參數小於2,那麼是取值操作
      if (arguments.length < 2) {
        var element = this[0]
        if (typeof property == 'string') {
          // 如果傳入是一個字符串,那就說明是取一個單獨的css樣式,返回結果
          if (!element) return
          return element.style[camelize(property)] || getComputedStyle(element, '').getPropertyValue(property)
        } else if (isArray(property)) {
          // 如果傳入的是一個數組,那麼返回一個對象,對象內容是數組所列的屬性與其對應值的鍵值對
          if (!element) return
          var props = {}
          var computedStyle = getComputedStyle(element, '')
          $.each(property, function(_, prop){
            props[prop] = (element.style[camelize(prop)] || computedStyle.getPropertyValue(prop))
          })
          return props
        }
      }

      var css = ''
      if (type(property) == 'string') {
        if (!value && value !== 0)
          this.each(function(){ this.style.removeProperty(dasherize(property)) })
        else
          css = dasherize(property) + ":" + maybeAddPx(property, value)
      } else {
        for (key in property)
          if (!property[key] && property[key] !== 0)
            this.each(function(){ this.style.removeProperty(dasherize(key)) })
          else
            css += dasherize(key) + ':' + maybeAddPx(key, property[key]) + ';'
      }

      return this.each(function(){ this.style.cssText += ';' + css })
    },

index

獲取一個元素的索引值。當elemen參數沒有給出時,返回當前元素在兄弟節點中的位置。當element參數給出時,返回它在當前對象集合中的位置。如果沒有找到該元素,則返回-1。

index: function(element){
      return element ? this.indexOf($(element)[0]) : this.parent().children().indexOf(this[0])
    },

hasClass

檢查對象集合中是否有元素含有指定的class。

hasClass: function(name){
      if (!name) return false
      return emptyArray.some.call(this, function(el){
        return this.test(className(el))
      }, classRE(name))
    },

addClass

爲每個匹配的元素添加指定的class類名。多個class類名使用空格分隔。

addClass: function(name){
      if (!name) return this
      return this.each(function(idx){
        if (!('className' in this)) return
        classList = []
        // 獲取當前元素的className,以及通過funcArg包裝參數,這樣name既可以是字符串也可以是回調函數
        var cls = className(this), newName = funcArg(this, name, idx, cls)
        // 對要設置的class newName進行分割後遍歷處理
        newName.split(/\s+/g).forEach(function(klass){
          // 當前元素不存在要添加的class的時候才往classList中push
          if (!$(this).hasClass(klass)) classList.push(klass)
        }, this)
        // 首先檢查classList的長度,如果長度爲0就沒有必要設置了,然後調用className函數進行class設置
        classList.length && className(this, cls + (cls ? " " : "") + classList.join(" "))
      })
    },

removeClass

移除當前對象集合中所有元素的指定class。如果沒有指定name參數,將移出所有的class。多個class參數名稱可以利用空格分隔。

removeClass: function(name){
      return this.each(function(idx){
        if (!('className' in this)) return
        if (name === undefined) return className(this, '')
        classList = className(this)
        funcArg(this, name, idx, classList).split(/\s+/g).forEach(function(klass){
          // 將匹配中的klass替換成' ',從而起到刪除的作用
          classList = classList.replace(classRE(klass), " ")
        })
        className(this, classList.trim())
      })
    },

toggleClass

在匹配的元素集合中的每個元素上添加或刪除一個或多個樣式類。如果class的名稱存在則刪除它,如果不存在,就添加它。如果 setting的值爲真,這個功能類似於 addClass,如果爲假,這個功能類似與 removeClass。

toggleClass: function(name, when){
      if (!name) return this
      return this.each(function(idx){
        // name 可以是字符串也可以是函數
        var $this = $(this), names = funcArg(this, name, idx, className(this))
        // 因爲有可能是切換多個class,所以切割之後遍歷處理
        names.split(/\s+/g).forEach(function(klass){
          // 當when沒有傳入的時候,進行的邏輯是元素有kClass,就移除,否則添加
          // 當指定了when轉換後爲真便是添加,否則移除kClass
          (when === undefined ? !$this.hasClass(klass) : when) ?
            $this.addClass(klass) : $this.removeClass(klass)
        })
      })
    },

scrollTop

獲取或設置頁面上的滾動元素或者整個窗口向下滾動的像素值。

scrollTop: function(value){
      if (!this.length) return
      // 當前元素是否有scrollTop屬性
      var hasScrollTop = 'scrollTop' in this[0]
      // 如果沒有傳入value值,則是取值操作,hasScrollTop爲真則返回元素的scrollTop屬性值,否則返回元素的pageYOffset屬性值
      if (value === undefined) return hasScrollTop ? this[0].scrollTop : this[0].pageYOffset
      return this.each(hasScrollTop ?
         // 如果有scrollTop屬性則直接設置該屬性對應的value值
        function(){ this.scrollTop = value } :
         // 否則調用元素的scrollTo方法,並傳入scrollX和value
        function(){ this.scrollTo(this.scrollX, value) })
    },

scrollLeft

獲取或設置頁面上的滾動元素或者整個窗口向右滾動的像素值。

scrollLeft: function(value){
      if (!this.length) return
      var hasScrollLeft = 'scrollLeft' in this[0]
      if (value === undefined) return hasScrollLeft ? this[0].scrollLeft : this[0].pageXOffset
      return this.each(hasScrollLeft ?
        function(){ this.scrollLeft = value } :

        function(){ this.scrollTo(value, this.scrollY) })
    },

offsetParent

找到第一個定位過的祖先元素,意味着它的css中的position 屬性值爲“relative”, “absolute” or “fixed”


rootNodeRE = /^(?:body|html)$/i,

offsetParent: function() {
      return this.map(function(){
        // 獲取集合中當前元素的有定位元素的最近的祖先元素,沒有獲取到則用body元素賦值
        var parent = this.offsetParent || document.body
        // 祖先元素存在,並且不是根元素,html或者body元素,並且parent的position屬性爲static則再次進入循環
        // 以上條件都不滿足則直接返回parent元素
        while (parent && !rootNodeRE.test(parent.nodeName) && $(parent).css("position") == "static")
          parent = parent.offsetParent
        return parent
      })
    }

position

獲取對象集合中第一個元素的位置。相對於 offsetParent。當絕對定位的一個元素靠近另一個元素的時候,這個方法是有用的。

position: function() {
      if (!this.length) return

      var elem = this[0],
        // 找到有定位屬性的祖先元素
        offsetParent = this.offsetParent(),
        // 獲取當前元素相對於document的位置
        offset       = this.offset(),
        // 獲取父元素相對於document的位置,如果是根元素(html或者body)則爲0, 0
        parentOffset = rootNodeRE.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset()

      // 相對於第一個定位祖先元素的位置關係不應該包括margin的舉例,所以減去
      offset.top  -= parseFloat( $(elem).css('margin-top') ) || 0
      offset.left -= parseFloat( $(elem).css('margin-left') ) || 0
      // 祖先定位元素加上border的寬度
      parentOffset.top  += parseFloat( $(offsetParent[0]).css('border-top-width') ) || 0
      parentOffset.left += parseFloat( $(offsetParent[0]).css('border-left-width') ) || 0
      // 計算結果
      return {
        top:  offset.top  - parentOffset.top,
        left: offset.left - parentOffset.left
      }
    },

detach

$.fn.detach = $.fn.remove

forEach

forEach: emptyArray.forEach,

reduce

reduce: emptyArray.reduce,

push

push: emptyArray.push,

sort

sort: emptyArray.sort,

splice

splice: emptyArray.splice,

indexOf

在當前對象集合中獲取一個元素的索引值。如果給定formindex參數,從該位置開始往後查找,返回基於0的索引值,如果沒找到,則返回-1。index 方法是基於這個方法實現的。

indexOf: emptyArray.indexOf,
參考資料 Zepto 1.2.0 中文版API手冊
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章