zepto源码阅读第四天

今天把 $.fn 的其余函数一起列出来哈。

get: function(idx){
   // 可选,不传时,将Zetpo转换成数组
   return idx === undefined ? slice.call(this) : this[idx >= 0 ? idx : idx + this.length]
 },
 toArray: function(){ return this.get() },
 size: function(){
   return this.length
 },
 remove: function(){
   return this.each(function(){
     if (this.parentNode != null)
       this.parentNode.removeChild(this)
   })
 },
 // 遍历集合,将集合中的每一项放入callback中进行处理,如果callback返回false,那么就会停止循环了
 each: function(callback){
   emptyArray.every.call(this, function(el, idx){
     return callback.call(el, idx, el) !== false
   })
   return this
 },
 filter: function(selector){
   // this.not(selector)取到需要排除的集合,第二次再取反(这个时候this.not的参数就是一个集合了),得到想要的集合
   if (isFunction(selector)) return this.not(this.not(selector))

   // 原生filter如果返回true就收集
   return $(filter.call(this, function(element){
     // 如果匹配就返回true
     return zepto.matches(element, selector)
   }))
 },
 add: function(selector,context){
   // 合并后并保证唯一
   return $(uniq(this.concat($(selector,context))))
 },
 is: function(selector){
   return typeof selector == 'string' ? this.length > 0 && zepto.matches(this[0], selector) : 
       selector && this.selector == selector.selector
 },
 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为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)
     this.forEach(function(el){
       if (excludes.indexOf(el) < 0) nodes.push(el)
     })
   }
   // 由于上面得到的结果是数组,这里需要转成zepto对象,以便继承其它方法,实现链写
   return $(nodes)
 },
 /*
   接收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()
   })
 },
 eq: function(idx){
   // 取Zepto中的指定索引的元素,再包装成Zepto返回
   return idx === -1 ? this.slice(idx) : this.slice(idx, + idx + 1)
 },
 first: function(){
   var el = this[0]
   // 这里不知道什么时候isObject(el)为false,el还可以是什么类型?
   return el && !isObject(el) ? el : $(el)
 },
 last: function(){
   var el = this[this.length - 1]
   return el && !isObject(el) ? el : $(el)
 },
 find: function(selector){
   var result, $this = this
   if (!selector) result = $()
   // 如果selector为对象,遍历selector,筛选出父级为集合中记录的selector
   else if (typeof selector == 'object')
     result = $(selector).filter(function(){
       var node = this
       // 如果$.contains(parent, node)返回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
 },
 closest: function(selector, context){
   var nodes = [], collection = typeof selector == 'object' && $(selector)
   this.each(function(_, node){
     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)
   })
   return $(nodes)
 },
 parents: function(selector){
   var ancestors = [], nodes = this
   while (nodes.length > 0)
     nodes = $.map(nodes, function(node){
       if ((node = node.parentNode) && !isDocument(node) && ancestors.indexOf(node) < 0) {
         ancestors.push(node)
         return node
       }
     })
   return filtered(ancestors, selector)
 },
 parent: function(selector){
   return filtered(uniq(this.pluck('parentNode')), selector)
 },
 children: function(selector){
   return filtered(this.map(function(){ return children(this) }), selector)
 },
 contents: function() {
   return this.map(function() { return this.contentDocument || slice.call(this.childNodes) })
 },
 siblings: function(selector){
   return filtered(this.map(function(i, el){
     // 到其父元素取得所有子节点,再排除本身
     return filter.call(children(el.parentNode), function(child){ return child!==el })
   }), selector)
 },
 empty: function(){
   return this.each(function(){ this.innerHTML = '' })
 },
 // `pluck` is borrowed from Prototype.js
 pluck: function(property){
   return $.map(this, function(el){ return el[property] })
 },
 show: function(){
   return this.each(function(){
     // 清除内联样式display="none"
     this.style.display == "none" && (this.style.display = '')
     // 计算样式display为none时,重赋显示值
     if (getComputedStyle(this, '').getPropertyValue("display") == "none")
       // defaultDisplay是获取元素默认display的方法
       this.style.display = defaultDisplay(this.nodeName)
   })
 },
 replaceWith: function(newContent){
   // 将要替换内容插到被替换内容前面,然后删除被替换内容
   return this.before(newContent).remove()
 },
 wrap: function(structure){
   var func = isFunction(structure)
   if (this[0] && !func)
     var dom   = $(structure).get(0),
         clone = dom.parentNode || this.length > 1

   return this.each(function(index){
     $(this).wrapAll(
       func ? structure.call(this, index) :
         clone ? dom.cloneNode(true) : dom
     )
   })
 },
 wrapAll: function(structure){
   if (this[0]) {
     $(this[0]).before(structure = $(structure))

     var children
     // drill down to the inmost element
     while ((children = structure.children()).length) structure = children.first()
     $(structure).append(this)
   }
   return this
 },
 wrapInner: function(structure){
   var func = isFunction(structure)
   return this.each(function(index){
     var self = $(this), contents = self.contents(),
         dom  = func ? structure.call(this, index) : structure
     contents.length ? contents.wrapAll(dom) : self.append(dom)
   })
 },
 unwrap: function(){
   this.parent().each(function(){
     $(this).replaceWith($(this).children())
   })
   return this
 },
 clone: function(){
   return this.map(function(){ return this.cloneNode(true) })
 },
 hide: function(){
   return this.css("display", "none")
 },
 toggle: function(setting){
   return this.each(function(){
     var el = $(this)
     ;(setting === undefined ? el.css("display") == "none" : setting) ? el.show() : el.hide()
   })
 },
 prev: function(selector){ return $(this.pluck('previousElementSibling')).filter(selector || '*') },
 next: function(selector){ return $(this.pluck('nextElementSibling')).filter(selector || '*') },
 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)
 },
 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: function(name, value){
   var result
   return (typeof name == 'string' && !(1 in arguments)) ?
     // 仅有name,且为字符串时,表示读,直接用getAttribute(name)读
     (0 in this && this[0].nodeType == 1 && (result = this[0].getAttribute(name)) != null ? result : undefined) :
     this.each(function(idx){
       if (this.nodeType !== 1) return
       // 如果name为对象,批量设置属性
       if (isObject(name)) for (key in name) setAttribute(this, key, name[key])
       // 处理value为函数 null/undefined 的情况
       else setAttribute(this, name, funcArg(this, value, idx, this.getAttribute(name)))
     })
 },
 removeAttr: function(name){
   return this.each(function(){ this.nodeType === 1 && name.split(' ').forEach(function(attribute){
     setAttribute(this, attribute)
   }, this)})
 },
 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: function(name){
   name = propMap[name] || name
   return this.each(function(){ delete this[name] })
 },
 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: 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: 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
   // 如果父元素是document
   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: function(property, value){
   // 只有一个参数的时候,只读
   if (arguments.length < 2) {
     var element = this[0]
     if (typeof property == 'string') {
       if (!element) return
       // getComputedStyle是一个可以获取当前元素所有最终使用的CSS属性值。返回的是一个CSS样式声明对象([object CSSStyleDeclaration]),只读
       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)
       // dasherize 是将字符串转换成css属性(background-color格式)
       this.each(function(){ this.style.removeProperty(dasherize(property)) })
     else
       css = dasherize(property) + ":" + maybeAddPx(property, value)
   } else {
     // property 是一个对象
     for (key in property)
       // 当property[key]的值为 null/undefined,删除属性
       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: function(element){
   // 这里的$(element)[0]是为了将字符串转成node,因为this是个包含node的数组
   // 当不指定element时,取集合中第一条记录在其父节点的位置
   // this.parent().children().indexOf(this[0])这句很巧妙,和取第一记录的parent().children().indexOf(this)相同
   return element ? this.indexOf($(element)[0]) : this.parent().children().indexOf(this[0])
 },
 hasClass: function(name){
   if (!name) return false
   // some ES5的新方法 有一个匹配,即返回true
   return emptyArray.some.call(this, function(el){
     // this是classRE(name)生成的正则
     return this.test(className(el))
   }, classRE(name))
 }
addClass: function(name){
   if (!name) return this
   return this.each(function(idx){
     // 已存在,返回
     if (!('className' in this)) return
     classList = []
     var cls = className(this), newName = funcArg(this, name, idx, cls)
     // 多个类,空格分隔为数组
     newName.split(/\s+/g).forEach(function(klass){
       if (!$(this).hasClass(klass)) classList.push(klass)
     }, this)
     // 设值
     classList.length && className(this, cls + (cls ? " " : "") + classList.join(" "))
   })
 },
 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){
       classList = classList.replace(classRE(klass), " ")
     })
     className(this, classList.trim())
   })
 },
 toggleClass: function(name, when){
   if (!name) return this
   return this.each(function(idx){
     var $this = $(this), names = funcArg(this, name, idx, className(this))
     names.split(/\s+/g).forEach(function(klass){
       // when 通过true/false控制
       (when === undefined ? !$this.hasClass(klass) : when) ?
         $this.addClass(klass) : $this.removeClass(klass)
     })
   })
 },
 scrollTop: function(value){
   if (!this.length) return
   var hasScrollTop = 'scrollTop' in this[0]
   if (value === undefined) return hasScrollTop ? this[0].scrollTop : this[0].pageYOffset
   return this.each(hasScrollTop ?
     // 支持scrollTop,直接赋值
     function(){ this.scrollTop = value } :
     // 不支持scrollTop,滚到指定坐标  pageYOffset 属性是 scrollY 属性的别名 pageYOffset兼容更好
     function(){ this.scrollTo(this.scrollX, value) })
 },
 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) })
 },
 position: function() {
   if (!this.length) return

   var elem = this[0],
     // Get *real* offsetParent
     // 读到父元素
     offsetParent = this.offsetParent(),
     // Get correct offsets
     // 读到坐标
     offset       = this.offset(),
     // 读到父元素的坐标
     parentOffset = rootNodeRE.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset()

   // Subtract element margins
   // note: when an element has margin: auto the offsetLeft and marginLeft
   // are the same in Safari causing offset.left to incorrectly be 0
   // 坐标减去外边框
   offset.top  -= parseFloat( $(elem).css('margin-top') ) || 0
   offset.left -= parseFloat( $(elem).css('margin-left') ) || 0

   // Add offsetParent borders
   // 加上父元素的border
   parentOffset.top  += parseFloat( $(offsetParent[0]).css('border-top-width') ) || 0
   parentOffset.left += parseFloat( $(offsetParent[0]).css('border-left-width') ) || 0

   // Subtract the two offsets
   return {
     top:  offset.top  - parentOffset.top,
     left: offset.left - parentOffset.left
   }
 },
 offsetParent: function() {
 /**
  * 返回第一个匹配元素用于定位的祖先元素
  * 原理:读取父元素中第一个其position设为relative或absolute的可见元素
  */
   return this.map(function(){
     // 读取定位父元素,没有,则body
     var parent = this.offsetParent || document.body
     // 如果找到的定位元素  position='static'继续往上找,直到body/Html
     while (parent && !rootNodeRE.test(parent.nodeName) && $(parent).css("position") == "static")
       parent = parent.offsetParent
     return parent
   })
 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值