zepto源碼研究 - zepto.js-4(經常使用的工具)

$.each:css

/**
   * 以集合每個元素做爲上下文,來執行回調函數
   * @param elements
   * @param callback
   * @returns {*}
   */
  $.each = function(elements, callback){
    var i, key
    if (likeArray(elements)) {        //數組、僞數組
      for (i = 0; i < elements.length; i++)
        if (callback.call(elements[i], i, elements[i]) === false) return elements
    } else {
      for (key in elements)       //對象
        if (callback.call(elements[key], key, elements[key]) === false) return elements
    }

    return elements
  }

這裏的elements能夠是數組或者對象,若是是對象,則會將其原型裏面的屬性也遍歷出來,最後返回elments自己,若是回調函數返回了false,則終止循環html

remove:node

/**
     * 刪除元素集
     * 原理   parentNode.removeChild
     * @returns {*}
     */
    remove: function(){
      //遍歷到其父元素   removeChild
      return this.each(function(){
        if (this.parentNode != null)
          this.parentNode.removeChild(this)
      })
    },

這裏的重點是parentNode.removeChild(this); 若是是沒有父節點的這裏不會執行。正則表達式

is:數組

//返回集合中的第1條記錄是否與selector匹配
    is: function(selector){
      return this.length > 0 && zepto.matches(this[0], selector)
    }

例如$("#id").is(".hasClass"); 實際就是調用matches進行匹配緩存

not: 
app

//排除集合裏知足條件的記錄,接收參數爲:css選擇器,function, dom ,nodeList
    not: function(selector){
      var nodes=[]
      //當selector爲函數時,safari下的typeof nodeList也是function,因此這裏須要再加一個判斷selector.call !== undefined
      if (isFunction(selector) && selector.call !== undefined)
        this.each(function(idx){
          //注意這裏收集的是selector.call(this,idx)返回結果爲false的時候記錄
          if (!selector.call(this,idx)) nodes.push(this)
        })
      else {
        //當selector爲字符串的時候,對集合進行篩選,也就是篩選出集合中知足selector的記錄
        var excludes = typeof selector == 'string' ? this.filter(selector) :
            //當selector爲nodeList時執行slice.call(selector),注意這裏的isFunction(selector.item)是爲了排除selector爲數組的狀況
            //當selector爲css選擇器,執行$(selector)
            (likeArray(selector) && isFunction(selector.item)) ? slice.call(selector) : $(selector);
        this.forEach(function(el){
          //篩選出不在excludes集合裏的記錄,達到排除的目的
          if (excludes.indexOf(el) < 0) nodes.push(el)
        })
      }
      return $(nodes)//因爲上面獲得的結果是數組,這裏須要轉成zepto對象,以便繼承其它方法,實現鏈寫
    },

這裏首先判斷selector是否爲函數,若是是,則遍歷執行函數,若是返回false,則push到數組中dom

若是css選擇字符串則調用this.filter(seelctor),這裏有個小技巧使得not和filter相互調用wordpress

若是是數組或對象,則直接返回$(他本身)函數

最後遍歷this,只要不是在excludes裏面的都是知足條件的

has:

/*
     接收node和string做爲參數,給當前集合篩選出包含selector的集合
     isObject(selector)是判斷參數是不是node,由於typeof node == 'object'
     當參數爲node時,只須要判讀當前記當裏是否包含node節點便可
     當參數爲string時,則在當前記錄裏查詢selector,若是長度爲0,則爲false,filter函數就會過濾掉這條記錄,不然保存該記錄
     */
    has: function(selector){
      return this.filter(function(){
        return isObject(selector) ?
            $.contains(this, selector) :
            $(this).find(selector).size()
      })
    },

重點提示下:這裏的this若是也知足selector,則最後返回的結果集裏也會有this  

show:

 

 
/**
* 獲取元素的默認display屬性
* 是爲了兼容什麼?
* @param nodeName
* @returns {*}
*/
function defaultDisplay(nodeName) {
var element, display
if (!elementDisplay[nodeName]) { //緩存裏沒有

element = document.createElement(nodeName)
document.body.appendChild(element)
display = getComputedStyle(element, '').getPropertyValue("display")
element.parentNode.removeChild(element)

// display == "none",設置成blaock,即隱藏-顯示
display == "none" && (display = "block")

elementDisplay[nodeName] = display //TODO:緩存元素的默認display屬性,緩存幹嗎?
}
return elementDisplay[nodeName]
}


/*
* * 展現 * @returns {*} */ show: function(){ return this.each(function(){ //清除內聯樣式display="none" this.style.display == "none" && (this.style.display = '') //計算樣式display爲none時,重賦顯示值 if (getComputedStyle(this, '').getPropertyValue("display") == "none") this.style.display = defaultDisplay(this.nodeName) //defaultDisplay是獲取元素默認display的方法 }) },

 

this.style.display = ' '是清除元素的style裏面的display樣式,恢復元素的默認樣式。

getComputedStyle(dom)能獲取dom的全部css樣式,第二個參數能夠不傳

getComputedStyle(dom,":after")能獲取dom的:after僞類的全部css樣式(這裏只談移動端)

詳情請看:http://www.zhangxinxu.com/wordpress/2012/05/getcomputedstyle-js-getpropertyvalue-currentstyle/

若是清除了style裏面的display屬性仍是無效的話,直接顯式的給display賦默認值,defaultDisplay的內部實際是在document裏面新添加一個元素,來獲取默認值的。

css:

/**
     * 讀寫樣式   寫:內聯樣式  讀:計算樣式
     *   原理 讀:elment[style]/getComputedStyle, 寫 this.style.cssText 行內樣式設值
     * @param property   String/Array/Fun
     * @param value
     * @returns {*}
     */
    css: function(property, value){
      //只有一個傳參,讀
      if (arguments.length < 2) {
        var computedStyle, element = this[0]
        if(!element) return
        //getComputedStyle是一個能夠獲取當前元素全部最終使用的CSS屬性值。返回的是一個CSS樣式聲明對象([object CSSStyleDeclaration]),只讀
        //讀到計算樣式
        computedStyle = getComputedStyle(element, '')
        //設置樣式
        if (typeof property == 'string')// 字符串
        //優先讀行內樣式,再讀計算樣式,行內樣式級別最高? TODO:彷佛有bug,若是設置了!important 呢
          return element.style[camelize(property)] || computedStyle.getPropertyValue(property)
        else if (isArray(property)) {     //數組
          var props = {}
          $.each(property, function(_, prop){     //遍歷讀取每一條樣式,存入JSON,返回
            props[prop] = (element.style[camelize(prop)] || computedStyle.getPropertyValue(prop))
          })
          return props
        }
      }

      //若是是寫
      var css = ''
      if (type(property) == 'string') {
        if (!value && value !== 0)  //null,undefined時,刪掉樣式
          this.each(function(){
            //刪除        dasherize是將字符串轉換成css屬性(background-color格式)
            this.style.removeProperty(dasherize(property))
          })
        else
        //‘-’格式值 + px單位
          css = dasherize(property) + ":" + maybeAddPx(property, value)
      } else {
        for (key in property)     //是對象時
          if (!property[key] && property[key] !== 0)
          //當property[key]的值爲null/undefined,刪除屬性
            this.each(function(){ this.style.removeProperty(dasherize(key)) })
          else
          //‘-’格式值 + px單位
            css += dasherize(key) + ':' + maybeAddPx(key, property[key]) + ';'
      }

      //設值   //TODO:   this.style.cssText +=  未考慮去重了
      return this.each(function(){ this.style.cssText += ';' + css })
    },

判斷只傳了一個參數:arguments.length < 2 或者 1 in  arguments,

流程大體以下:

1:判斷參數,若是是一個參數則爲只讀模式

1.2:若是property爲字符串,則 直接獲取元素的css屬性值

1.3:若是property爲數組,則this.each遍歷數組,獲取每一個屬性的value值並造成props對象返回

 

2:如有2個參數,爲寫模式

2.1:若是property爲字符串,則style.cssText+= property:value(這裏的處理沒有去重,但本人測了一下,這並不影響頁面的渲染)

2.2:若是property爲對象,則循環遍歷property

這裏有幾個函數要提一下:

camelize() 將帶「—」的轉爲駝峯命名例如「background-color」轉爲「backgroundColor」,對象屬性是必須駝峯命名的。

dasherize()  將「backgroundColor」轉爲「background-color」,內部實現是用正則表達式匹配

style.removeProperty  刪除style中的某個屬性

style.setProperty(property,value,priority)  設置style中的某個屬性,priority可取「important」 ,(不太明白爲何zepto不用這個方法,望高人指教)

maybeAddPx (property,value):根據property類型給value加上「px」後綴

html:

 
 
/**
* 處理 arg爲函數/值
* 爲函數,返回函數返回值
* 爲值,返回值
* @param context
* @param arg
* @param idx
* @param payload
* @returns {*}
*/
function funcArg(context, arg, idx, payload) {
return isFunction(arg) ? arg.call(context, idx, payload) : arg
}
/**
     * 讀寫元素HTML內容
     * 原理 經過innerHTML讀內容,append()寫內容
     * @param html
     * @returns {*|string|string|string|string|string}
     */
    html: function(html){
      return 0 in arguments ?
          this.each(function(idx){
            var originHtml = this.innerHTML   //記錄原始的innerHTMl
            //若是參數html是字符串直接插入到記錄中,
            //若是是函數,則將當前記錄做爲上下文,調用該函數,且傳入該記錄的索引和原始innerHTML做爲參數
            $(this).empty().append( funcArg(this, html, idx, originHtml) )
          }) :
          (0 in this ? this[0].innerHTML : null)
    },

html():不傳參數則獲取元素的innerHTML,

html("<div></div>"):設置元素的innerHTML

html(function(){....}):可傳入方法,返回值設置爲元素的innerHTML

這裏的funcArg的功能是判斷html爲函數則以this爲上下文,以idx,originHtml爲參數執行函數

text:

/**
     * 讀寫元素文本內容
     * 原理:  經過 textContent 讀寫文本
     * @param text
     * @returns {*}
     */
    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[0].textContent : null)   //未傳參讀
    },
相關文章
相關標籤/搜索