Zepto源码之$.fn工具函数

本文详细解析 Zepto.js 框架中 $.fn 的工具函数,包括 get、toArray、concat、map、slice、ready 等一系列操作 DOM 元素的方法,涵盖选择、遍历、操作、过滤、属性处理等多个方面,帮助开发者深入理解 Zepto.js 的内部工作机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

$.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手册
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值