Zepto源码解读
/******************************************************************************
*Zepto核心和dom操作
********************************************************************************/
;var Zepto = (function(){
var undefined,key,$,classList,emptyArray = [],slice = emptyArray.slice,
document = window.document,
elementDisplay = {},classCache = {},
getComputedStyle = document.defaultView.getComputedStyle,
//属性值不需要加px的css样式集;
cssNumber = {'column-count':1,'columns':1,'font-weight':1,'line-height':1,'opacity':1,'z-index':1,'zoom':1},
fragmentRe = /^\s*<(\w+|!)[^>]*>/,
// Used by `$.zepto.init` to wrap elements, text/comment nodes, document,
// and document fragment node types.
elementType = [1,3,8,9,11],
adjacencyOperators = ['after','prepend','before','append'],
//Containers about table
table = document.createElement('table'),
tableRow = document.createElement('tr'),
containers = {
'tr':document.createElement('tbody'),
'tbody':table,'tfoot':table,'thead':table,
'td':tableRow,'th':tableRow,
'*':document.createElement('div')
},
//about download
readyRe = /complete|loaded|interactive/,
//The RegExp of className id and tagName
classSelectorRe = /^\.([\w-]+)$/,
idSelectorRe = /^#([\w-]+)$/,
tagSelectorRe = /([\w-]+)$/,
toString = ({}).toString,
zepto = {},
camelize,uniq,
tempParent = document.createElement('div');
//判断一个元素是否匹配给定的选择器
zepto.matchs = function(element,selector) {
//如果element参数不存在,或者nodeType不为1(元素节点)
if(!element || element.nodeType !== 1) return false
//引用不同浏览器的matchsSelector方法
var matchsSelector = element.webkitMatchesSelector || element.mozMatchesSelector
|| element.oMatchesSelector || element.MatchesSelector;
//如果matchesSelector被浏览器支持,直接返回结果
if(matchsSelector) return matchsSelector.call(element,selector)
//如果浏览器不支持matchesSelector,则将节点放入一个临时的div中,
//再通过selector来查找这个div下的节点集,再判断给定的element是否在节点集中,如果在
//则返回一个非零(非false)的数字
//fall back to performing a selector
var match ,parent = element.parentNode, temp = !parent;
//如果element没有父节点,就将他插入一个临时的div中
if(temp){
(parent = tempParent).appendChild(element);
}
//将parent作为上下文,调用qsa函数,查找Selector匹配结果,返回一个数组,
//获得element在数组中的索引,不存在时为-1;
//通过~运算符~-1转成0,存在时转成一个非0数;返回转后的数;
match = ~zepto.qsa(parent,selector).indexOf(element);
//将插入节点删掉
temp && tempParent.removeChild(element);
return match;
}
//Judge a Object type
//判断书否为函数
function isFunction(value) { return toString.call(value) == "[object Function]" }
//判断是否为对象
function isObject(value) { return value instanceof Object }
//判断是否是一个纯粹的对象
//对于通过字面量定义的对象和new Object的对象返回true
//new Object('aaa')==>false,new Object({'a':1})==>true
function isPlainObject(value) {
var ctor,key;
//判断value书否为对象,为否则返回false;
if(toString.call(value) !== '[object Object]') return false
//如果value的构造函数是函数,则把构造函数的原形对象赋值给ctor;
ctor = (isFunction(value.constructor) && value.constructor.prototype)
//如果ctor不存在或者’isPrototypeOf‘不是ctor的自由属性,返回false;
if(!ctor || !hasOwnProperty.call(ctor,'isPrototypeOf')) return false
//如果key未定义(对象为空)或是value的自由属性,则返回true;
for(key in value);
return key ===undefined || hasOwnProperty.call(value,key)
}
//判断对象是否为数组
function isArray(value) { return value instanceof Array }
//判断对象是否为类数组
function likeArray(value) { return typeof value.length == 'Number' }
//压缩数组,剔除null和undefined,0和''都代表null
function campact(array) { return array.filter(function(item){ return item !== undefined && item !== null })}
//扁平化数组,数组副本
function flatten(array) { return array.length > 0 ? [].concat.apply([],array) : array }
//转换成驼峰命名
camelize = function(str) { return str.replace(/-+(.)/g,function(match,chr){ return chr ? chr.toUpperCase() : '' }) }
//把驼峰命名转换成中划线
function dasherize(str) {
return str.replace(/::/,'/')
.replace(/([A-Z]+)([A-Z][a-z])/g,'$1_$2') //在大写小写之间插入下划线 例如 ADCaaa==>AD_Caaa
.replace(/([a-z]+)([A-Z])/g,'$1_$2') //在小写大写之间插入下划线 例如 adcfA==>adcf_A
.replace(/_/g,'-') //把下划线转换成中划线
.toLowerCase();
}
//数组去重 如果某条数据在数组中的位置和通过indexOf获得的索引值有不同时,就是重复数据
uniq = function(array) { return array.filter(function (item,index) { return array.indexOf(item) == index }) }
//把给定的参数生成正则,并缓存到classCache里
function classRe(name) {
return name in classCache ?
classCache[name] : (classCache[name] = RegExp('(^|\\s)'+name+'(\\s|$)'));
}
//给需要的样式值后面加上px值,除了cssNumber里面指定的
function maybeAddPx(name,value) {
return (typeof value == 'number' && !cssNumber[dasherize(name)]) ? value + 'px' : value;
}
//获得元素的display属性值,把所有的属性值存到缓存区elementDisplay中并值默认设为block;
function defaultDisplay(nodeName) {
var element,display;
//如果elementDisplay中不存在该元素的值
if(!elementDisplay[nodeName]) {
//创建一个临时元素,并添加到body中
element = document.createElement(nodeName);
document.body.appendChild(element);
//获得该元素的最终display值
display = getComputedStyle(element,'').getPropertyValue('display');
//删除临时元素element
element.parentNode.removeChild(element);
//把display的值设为block
display == 'none' && (display = 'block');
//把display值加入到 elementDisplay;
elementDisplay[nodeName] = display;
}
return elementDisplay[nodeName];
}
//根据给定的html字符窜,生成dom node 数组
// `$.zepto.fragment` takes a html string and an optional tag name
// to generate DOM nodes nodes from the given html string.
// The generated DOM nodes are returned as an array.
// This function can be overriden in plugins for example to make
// it compatible with browsers that don't support the DOM fully.
function fragment(html,name) {
//如果name参数没传,就提取html中匹配的第一个标签
if(name === undefined) name = fragmentRe.test(html) && RegExp.$1;
//如果name在containers中不存在,设为*
if(!(name in containers)) name = '*'
//获得容器
var container = containers[name];
//把html插入容器
container.innerHtml = '' + html;
//返回Dom数组,并删除document中的节点
return $.each(slice.call(container.childNodes),function(){
container.removeChild(this);
})
}
//让dom元素继承$.fn的方法
// `$.zepto.Z` swaps out the prototype of the given `dom` array
// of nodes with `$.fn` and thus supplying all the Zepto functions
// to the array. Note that `__proto__` is not supported on Internet
// Explorer. This method can be overriden in plugins.
zepto.Z = function(dom,selector) {
dom = dom || [];
//把dom的__proto__属性指向调用函数zepto.Z(arguments.callee.prototype)的原形,然后把zepto.Z.prototype = $.fn;
//从而使dom继承$.fn上的所有方法
dom.__proto__ = arguments.callee.prototype;
dom.selector = selector || '';
return dom;
}
//判断对象的原形是否是zepto.Z
// `$.zepto.isZ` should return `true` if the given object is a Zepto
// collection. This method can be overriden in plugins.
zepto.isZ = function(object) {
return object instanceof zepto.Z
}
//定义$(selector,context)方法的函数定义
//1)$(selector)获得zepto的dom节点集
//2)$(selector,context)创建一个zepto元素
//3)$(function)实现window.onload事件
// `$.zepto.init` is Zepto's counterpart to jQuery's `$.fn.init` and
// takes a CSS selector and an optional context (and handles various
// special cases).
// This method can be overriden in plugins.
zepto.init = function (selector,context) {
// If nothing given, return an empty Zepto collection
if(!selector) return zepto.Z()
// If a function is given, call it when the DOM is ready
//如果参数是一个函数,当页面DOM加载完调用
else if(isFunction(selector)) return $(document).ready(selector)
// If a Zepto collection is given, juts return it
//如果selector已经是zepto的节点集,直接返回selector
else if(zepto.isZ(selector)) return selector
else {
var dom;
// normalize array if an array of nodes is given
//如果selector是一个正常的dom 节点数组,剔除数组中的null和undefined;
if (isArray(selector)) dom = campact(selector)
// if a JavaScript object is given, return a copy of it
// this is a somewhat peculiar option, but supported by
// jQuery so we'll do it, too
// 如果selector是一个纯对象,直接将他的副本放到数组中赋值给dom
else if(isPlainObject(selector)) dom = [$.extend({},selector)],selector = null
// wrap stuff like `document` or `window`
// 如果selector的nodeType值属于elementTypes,或selector==window
// 直接把selector放入数组中
else if(elementType.indexOf(selector.nodeType) >= 0 || selector ===window)
dom = [selector],selector = null
// If it's a html fragment, create nodes from it
// 如果selector是一个html的片段,就创建一个元素节点
else if(fragmentRe.test(selector))
dom = zepto.fragment(selector.trim(),RegExp.$1),selector = null
// If there's a context, create a collection on that context first, and select
// nodes from there
else if(context !== undefined) return $(context).find(selector)
// And last but no least, if it's a CSS selector, use it to select nodes.
// 如果selector是CSS选择器,调用zepto.qsa函数获得DOM节点集
else dom = zepto.qsa(document,selector)
// create a new Zepto collection from the nodes found
// 通过找到的DOM,创建一个新的zepto元素集并返回
return zepto.Z(dom,selector)
}
}
// `$` will be the base `Zepto` object. When calling this
// function just call `$.zepto.init, whichs makes the implementation
// details of selecting nodes and creating Zepto collections
// patchable in plugins.
// 把$ 设置为Zepto的基础对象,通过吊$就你实现调用$.zepto.init
$ = function(selector,context) {
return zepto.init(selector,context)
}
// Copy all but undefined properties from one or more
// objects to the `target` object.
// 把一个或多个资源对象的属性拷贝到目标对象上
$.extend = function(target) {
slice.call(arguments,1).forEach(function(source){
var key
for(key in source){
if(source[key] !== undefined){
target[key] = source[key]
}
}
})
return target;
}
// `$.zepto.qsa` is Zepto's CSS selector implementation which
// uses `document.querySelectorAll` and optimizes for some special cases, like `#id`.
// This method can be overriden in plugins.
zepto.qsa = function(element,selector) {
var found;
//当element为document,且Selector为id选择器时:
return (element === document && idSelectorRe.test(selector))?
//把通过getElementById找到的值放在数组中,然后返回,如果没有,返回空数组emptyArray
((found = element.getElementById(RegExp.$1))?[found]:emptyArray):
//当element不为元素节点和document时,返回空数组
(element.nodeType !== 1 && element.nodeType !== 9)?emptyArray:
//否则将得到的结果转换为数组再返回
slice.call(
//如果Selector为class选择器,直接调用getElementsByClassName
classSelectorRe.test(selector)?element.getElementsByClassName(selector):
//如果Selector为tag选择器,直接调用getElementsByClassName
tagSelectorRe.test(selector)?element.getElementsByTagName(selector):
//否则调用querySelectorAll
element.querySelectorAll(selector)
)
}
//在nodes的结果中进行过滤
function filtered(nodes,selector) {
return selector === undefined ? $(nodes) : $(nodes).filter(selector);
}
//这个函数在整个库中起着重要作用,判断arg为函数或者为值的情况
//下面很多设置元素属性时的函数都有用到
function funcArg(context,arg,idx,payload) {
return isFunction(arg) ? arg.call(context,idx,payload) : arg
}
//把一些方法挂载到$对象上
$.isFunction = isFunction
$.isObject = isObject
$.isArray = isArray
$.isPlainObject = isPlainObject
//获取某个值在数组中的索引
$.inArray = function(elem,array,i) {
return emptyArray.indexOf.call(array,elem,i)
}
//去除字符串头尾空格
$.trim = function(str){ return str.trim() }
// plugin compatibility
$.uuid = 0
//遍历elements,将每条记录放入callback里处理,把callback返回值不为null和undefined的结果
//保存的一个数组中,最后返回数组
$.map = function(elements,callback) {
var value,values=[],key,i
//如果被遍历的数据是数组或者类数组
if(likeArray(elements)){
for(i = 0;i < elements.length;i++){
value = callback(elements[i],i)
if(value != null) values.push(value)
}
}
//如果是对象
else{
for(key in elements) {
value = callback(elements[key],key)
if(value != null) values.push(value)
}
}
return values
}
//遍历数组,将每条数据作为callback的上下文,传入数据和数据的索引进行处理,如果有一条数据的处理结果
//返回的是false,停止变量,并返回elements
$.each = function(elements,callback) {
var key,i
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;
}
// 定义能被所有zepto的DOM元素使用的方法
// Define methods that will be available on all Zepto collections
zepto.fn = {
// Because a collection acts like an array
// copy over these useful array functions.
// ECMA5 中一些Array(数组)API
forEach:emptyArray.forEach,
reduce:emptyArray.reduce,
push:emptyArray.push,
concat:emptyArray.concat,
indexOf:emptyArray.indexOf,
// `map` and `slice` in the jQuery API work differently
// from their array counterparts
map:function(fn){
return $.map(this,function(el,i){
return fn.call(el,i,el);
})
},
slice:function(){
return $(slice.apply(this,arguments))
},
//DOM 的ready事件
ready:function(callback){
if(readyRe.test(document.readyState)) callback($)
else document.addEventListener('DOMContentLoaded',function(){ callback($)},false)
return this
},
//取集合中对应索引的值,如果没有参数,返回整个集合转换的数组
get:function(idx) {
return idx === undefined ? slice.call(this) : this[idx]
},
//把集合转换成数组
toArray:function(){
return this.get()
},
//获取集合的长度
size:function(){ return this.length },
//将集合从DOM中删除
remove:function(){
return this.each(function(){
if(this.parentNode != null) this.parentNode.removeChild(this)
})
},
//遍历集合,将集合的每一项放入callback中,去掉结果为false的,
//如果明确返回false,遍历就停止了
each:function(){
this.forEach(function(el,idx) {
callback.call(el,idx,el);
})
return this;
},
//过滤集合,返回能与selector匹配的
filter:function(selector) {
return $([].filter.call(this,function(element){
return zepto.matchs(element,selector)
}))
},
//将由selector获得的不同的节点追加到集合中
add:function(selector,context) {
//concat连接两个集合,应为由$()获得的集合是个数组
//uniq去重
return $(uniq(this.concat($(selector,context))))
},
//判断集合中的第一条数据是否和selector匹配
is:function(selector) {
return this.length > 0 && zepto.matchs(this[0],selector);
},
//排除集合里符合selector条件的记录,接收参数可以是function,CSS选择器,dom,nodelist
not:function(selector) {
var nodes = []
//当selector为函数时,safari下的typeof odeList也是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 exclude = 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(exclude.indexOf(el) < 0) nodes.push(el)
})
}
//由于上面得到的结果是数组,这里需要转成zepto对象,以便继承其它方法,实现链写
return $(nodes)
},
//根据集合的索引选取对应的记录,当索引为-1时,选择最后一个元素
eq:function (idx) {
return idx === -1 ? this.slice(idx) : this.slice(idx,+idx+1)
},
//取集合中的第一条数据
first:function() {
var el = this[0]
return el && !isObject(el) ? el : $(el)
},
//取集合中的最后一条数据
last:function() {
var el = this[this.length - 1];
return el && !isObject(el) ? el : $(el)
},
//在当集合中的匹配selector的子集
find:function(selector) {
var result
if(this.length === 1) result = zepto.qsa(this[0],selector)
else result = this.map(function(){ result zepto.qsa(this ,selector)})
return $(result)
},
//从当前元素遍历向上找到第一个匹配的元素
closest:function(selector,context) {
var node = this[0]
while(node && !zepto.matchs(node,selector))
node = node !== context && node !== document && node.parentNode
return $(node)
},
//取集合所有父级元素
parents:function(selector) {
var ancestors = [],nodes = this;
while(nodes.length > 0){
nodes = $.map(nodes,function(node){
if((node = node.parentNode) && node !== document && 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 slice.call(this.children)}), selector);
},
//获取元素的兄弟节点。
siblings:function(selector) {
return filtered(this.map(function(i,el){
//找到当前元素的父元素的子元素,且不等当前元素,就是他所有的相邻元素
return slice.call(el.parent.children).filter(function(child){ return child !== el})
}),selector)
}
//清除集合中每个元素的DOM节点。
empty:function() {
return this.each(function(){ this.innerHTML = '' })
},
//获取集合元素指定的属性值
pluck:function(property) {
return this.map(function(){ return this[property] });
},
//恢复元素display的默认值。
show:function() {
return this.each(function() {
//当内联的display == ‘none’时,清除该属性
this.style.display == 'none' && (this.style.display = null)
//如果该元素在样式表里的属性为none,把display的属性改为默认属性
if(getComputedStyle(this,'').getPropertyValue('display') == 'none') {
this.style.display = defaultDisplay(this.nodeName);
}
})
},
//用新的内容替换集合中的元素。
replaceWith:function(newContent) {
return this.before(newContent).remove();
},
//给集合的每一个元素加一个wrap。
wrap:function(newContent) {
return this.each(function(){
$(this).warpAll($(newContent)[0].cloneNode(false))
})
},
//将所有元素用一个Wrap包装起来。
wrapAll:function(newContent) {
if(this[0]) {
//把wrap插入到第一个元素前面
$(this[0]).before(newContent = $(newContent));
//把集合插入到wrap里
newContent.append(this)
}
return this
},
//移出元素的父节点,并加元素添加到原先自身父节点的位置。
unwrap:function() {
//找到集合内各元素对应的父节点
this.parent().each(function(){
//把父节点用对应的子元素替换
$(this).replaceWith($(this).children())
})
return this;
},
//clone 节点
clone:function() {
return $(this.map(function(){ return this.cloneNode(true) }))
},
//hide,隐藏元素
hide:function() {
return this.css('display','none')
},
//切换元素的显示隐藏,设置setting为true,显示元素,反之则隐藏元素。
toggle:function(setting) {
//当setting不存在时,display为none时,启动show(),否则hide()
//当setting为true时show(),为false时hide()
return (setting === undefined ? this.css('display') == 'none' : setting) ? this.show() : this.hide()
},
//获取集合中每个元素的前一个兄弟节点,可以传入选择器参数。
prev:function() { return $(this.pluck('previousElementSibling')) },
//获取下一个兄弟节点,可以传入一个选择器参数。
next:function() { return $(this.pluck('nextElementSibling'))},
//读写元素的innerHTML值, value为空,返回首个元素的innerHTML.若value不为空,则修改每个元素的innerHTML值。
html:function(html) {
//判断html是否定义
return html === undefined ?
//如果未定义,获取集合中第一个元素的html
(this.length>0 ? this[0].innerHTML : null) :
//如果html定义了
//html是字符窜,直接插入到每条记录中国
//如果是函数,则将当前记录作为上下文,调用该函数,且传入该记录的索引和原始innerHtml作为参数
this.each(function(idx) {
var originHtml = this.innerHTML;
$(this).empty().append( funcArg(this,html,idx,originHtml) )
})
},
//读写元素的text内容。
text:function(text) {
//如果不给定text参数,则为获取功能,集合长度大于0时,取第一条数据的textContent,否则返回null,
//如果给定text参数,则为集合的每一条数据设置textContent为text
return text === undefined ?
(this.length >0 ? this[0].textContent : null) :
this.each(function(){ this.textContent = text })
},
//读或写DOM属性。当没有给出value值的时候,从集合的第一个元素中读取特定属性;
//当给出value值的时候,修改集合中每个元素的特定属性值;
//当value值为null,则删除属性。多个属性可用对象来传递。
attr:function(name,value) {
var result
return (typeof name == 'String' && value === undefined) ?
//当name存在且为字符串,value不存在,表示获得第一条记录的属性
//集合为空或者node不是元素类型,返回undefined
(this.length == 0 || this[0].nodeType !== 1 ? undefined :
//如果取的是input的value时如下:
name == 'value' && this[0].nodeName == 'INPUT' ? this[0].value :
//如果浏览器不支持getAttribute,就用this[0][name]
(!(result = this[0].getAttribute(name)) && name in this[0]) ? this[0][name] : result
) :
//name 不为字符串,或者value存在时
this.each(idx) {
if(this.nodeType !== 1) return
//如果name是对象,则给每条记录设置属性
if(isObject(name)) for (var key in name) this.setAttribute(key name[key])
//如果name只是一个普通的属性字符串,用funcArg来处理value是值或者function的情况最终返回一个属性值
//如果funcArg函数返回的是undefined或者null,则相当于删除元素的属性
else this.setAttribute(name,funcArg(this,value,idx,this.getAttribute(name)))
}
},
//移出所有元素中指定的属性。
removeAttr:function(name) {
return this.each(function(){ if (this.nodeType === 1) this.removeAttribute(name) })
},
//这个方法在读取像checked和selected等在不同时间可能随着用户行为而变化的属性值时,方法更优先使用。
//获取第一条数据的指定的name属性或者给每条数据添加自定义属性,注意和setAttribute的区别
prop:function(name,value) {
//没有给定value时,为获取,给定value则给每一条数据添加,value可以为值也可以是一个返回值的函数
(value === undefined) ?
(this[0] ? this[0][name] : undefined) :
this.each(function(idx) {
this[name] = funcArg(this,value,idx,this[name])
})
},
//读写DOM元素的data-*属性。
data:function(name,value) {
//通过调用attr方法来实现获取与设置的效果,注意attr方法里,当value存在的时候,
//返回的是集合本身,如果不存在,则是返回获取的值
var data = this.attr('data-'+dasherize(name),value);
return (data !== null) ? data : undefined;
},
//读写元素的value值。
val:function(value) {
//如果value存在时,给每条记录设置value
//当value不存在时,获取value值
return (value === undefined) ?
(this.length>0 ? this[0].value : undefined) :
this.each(function(idx) {
this.value = funcArg(this,value,idx,this.value)
})
},
//获取元素在文档中的位置,返回值属性包含:top, left, width and height。
offset:function(){
if(this.length ===0) return null;
//如果集合不为空,获得第一个记录的位置矩阵
var obj = this[0].getBoundingClientRect();
//返回一条记录的offset,包括offsetTop,offsetLeft,offsetWidth,offsetHeight
return {
//在非ie下,obj.left相当对于窗口上部的距离,window.pageXOffset代表滚动的距离
left:obj.left+window.pageXOffset,
top:obj.top+window.pageYOffset,
width:obj.width,
height:obj.height
}
},
//读写DOM元素的CSS属性。
css:function(property,value) {
//如果value未定义,property存在且为字符串,返回集合第一条记录的对应样式值
if(value === undefined && typeof property == 'string') {
return (this.length === 0
? undefined
: this[0].style[camelize(property)] || getComputedStyle(this[0],'').getPropertyValue(property))
}
var css = '',key
//当property为对象时
for(key in property) {
//当一对map的value为空时,删除该属性
if(typeof property[key] == 'string' && property[key] == '')
this.each(function(){ this.style.removeProperty(dasherize(key))})
//否则给所有的记录添加改样式
else
css += dasherize(key) + ":" + maybeAddPx(key,property[key]) + ";"
}
//当property存在,且value也存在
if(typeof property == 'string') {
//当value值为空时
if(value === '')
this.each(function(){ this.style.removeProperty(dasherize(property))})
//否则给所有的记录添加改样式
else
css += dasherize(property) + ":" + maybeAddPx(property,value) + ";"
}
//通过cssText的方法绑定样式
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])
},
//检查集合中首个元素是否含有指定类名,返回一个bealoon值。
hasClass:function(name) {
if(this.length < 1) return false
//classRe用于生成正则
else return classRe(name).test(this[0].className)
},
//给集合中每个元素添加类名,多个类名可用空格分隔开。
addClass:function(name) {
return this.each(function(idx) {
classList = []
var cls = this.className,newName = funcArg(this,name,idx,cls)
//当name中同时有多个类名时
//先通过空格把name切分成数组
newName.split(/\s+/g).forEach(function(klass){
//如果当前记录不含klass类名,把klass加入到classLisst里
if(!(this.hasClass(klass))) classList.push(klass)
},this)//forEach中接收第二个参数this改变回调函数里this的指向
classList.length && this.className += (cls ? " " : "") + classList.join(" ")
})
},
//移出所有元素中指定的类。
removeClass:function(name) {
return this.each(function(idx) {
//如果name未定义,把class设为空
if(name === undefined)
return this.className = ''
classList = this.className
funcArg(this,name,idx,classList).split(/\s+/g).forEach(function(klass){
//如果有klass能和classList匹配,就替换成空格
classList = classList.replace(classRe(klass),' ')
})
this.className = classList.trim()
})
},
//切换元素的类名,若存在,则删除该类名,反之,则添加该类名。
toggleClass:function(name when) {
return this.each(function(idx) {
var newName = funcArg(this,name,idx,this.className)
;(when === undefined ? !$(this),hasClass(name) : when) ?
$(this).addClass(newName) : $(this).removeClass(newName)
})
}
}
// Generate the `width` and `height` functions
// 生成width和height 的函数
['width','height'].forEach(function(dimension) {
$.fn[dimension] = function(value) {
//把数组中的值的第一个字母转换成大写,并返回到Dimension中
var offset,Dimension = dimension.replace(/./,function(m) { return m[0].toUpperCase() });
//当没有传参数时
if(value === undefined) {
//当this[0]为window时,通过inner的方法获得宽高
return this[0] == window ? window.['inner'+Dimension] :
//当this[0]为document时,通过offset获得高宽
this[0] == document ? document.documentElement['offset'+Dimension] :
(offset = this.offset()) && offset[dimension]
//当传了参数时 设置元素的高宽
} else {
return this.each(function(idx) {
var el = $(this)
el.css(dimension,funcArg(this,value,idx,el[dimension]()))
})
}
}
})
// insert function 实现节点插入的函数
//@operator 操作符下标 0=>after,1=>prepend,2=>before,3=>append;
//@target 文档中已经存在的基节点
//@node 要插入的节点
function insert(operator,target,node) {
//确定要插入的父节点,如果operator为0、2就是target本身,否则就为target的parentNode
var parent = (operator%2) ? target : target.parentNode;
//如果parent存在就添加,否则就删除node
parent ? parent.insertBefore(node,
!operator ? target.nextSibling : //operator = 0 :after
operator == 1 ? parent.firstChild : //operator = 1 :prepend
operator == 2 ? target : //operator = 2 :before
null) : //operator = 3 :append
$(node).remove
}
//让node和node的子元素都经过traverseNode的处理
function traverseNode(node,fun) {
fun(node);
for(var key in node.childNodes) traverseNode(node.childNodes[key],fun)
}
// Generate the `after`, `prepend`, `before`, `append`,
// `insertAfter`, `insertBefore`, `appendTo`, and `prependTo` methods.
adjacencyOperators.forEach(function(key,operator) {
//实现 `after`, `prepend`, `before`, `append`
$.fn[key] = function() {
// arguments can be nodes, arrays of nodes, Zepto objects and HTML strings
// 参数可以是集合,集合数组,zepto节点,html字符串
// 先把参数转换成zepto的集合
var nodes = $.map(arguments,function(n){ return isObject(n) ? n : zepto.fragment(n) });
if(nodes.length < 1) return this;
//当nodes的长度大于0时
var size = this.length, copyByClone = size > 1, inReverse = operator < 2;
return this.each(function(index,target) {
for(var i = 0;i<nodes.length;i++) {
//保证节点插入文档后顺序不变
var node = nodes[inReverse ? nodes.length-i-1 : i]
//插入节点后,如果被插入的节点是SCRIPT,则执行里面的内容并将window设为上下文
traverseNode(node,function(node) {
if(node.nodeName !== null && node.nodeName.toUpperCase() === 'SCRIPT' && (!node.type || node.type = 'text/javascript'))
window['evel'].call(window,node.innerHTML)
})
//当基集合的长度大于1时,且index小于nodes.length-1时,就要克隆一个node
if(copyByClone && index < nodes.length - 1) node = node.cloneNode(true) ;
//插入节点
insert(operator,target,node)
}
})
}
//实现`insertAfter`, `insertBefore`, `appendTo`, and `prependTo` methods.
$.fn[(operator%2) ? key + 'To' : 'insert' + (operator ? 'Before' : 'After')] = function(html) {
$(html)[key](this)
return this
}
})
//把构造函数Z的原形设置为$.fn
zepto.Z.prototype = $.fn;
// Export internal API functions in the `$.zepto` namespace
// 把内部的API暴露出来
zepto.camelize = camelize;
zepto.uniq = uniq;
$.zepto = zepto;
return $;
}())
// If `$` is not yet defined, point it to `Zepto`
// 如果$还未定义,把Zepto赋值给$;
window.Zepto = Zepto;
'$' in window || window.$ = Zepto
Zepto源码解读的更多相关文章
- 第二十五课:jQuery.event.trigger的源码解读
本课主要来讲解jQuery.event.trigger的源码解读. trigger = function(event, data, elem, onlyHandlers){ if(elem & ...
- 读 zepto 源码之工具函数
Zepto 提供了丰富的工具函数,下面来一一解读. 源码版本 本文阅读的源码为 zepto1.2.0 $.extend $.extend 方法可以用来扩展目标对象的属性.目标对象的同名属性会被源对象的 ...
- 读Zepto源码之集合操作
接下来几个篇章,都会解读 zepto 中的跟 dom 相关的方法,也即源码 $.fn 对象中的方法. 读Zepto源码系列文章已经放到了github上,欢迎star: reading-zepto 源码 ...
- 读 Zepto 源码之集合元素查找
这篇依然是跟 dom 相关的方法,侧重点是跟集合元素查找相关的方法. 读Zepto源码系列文章已经放到了github上,欢迎star: reading-zepto 源码版本 本文阅读的源码为 zept ...
- 读zepto源码之工具函数
读zepto源码之工具函数 Zepto 提供了丰富的工具函数,下面来一一解读. 源码版本 本文阅读的源码为 zepto1.2.0 $.extend $.extend 方法可以用来扩展目标对象的属性.目 ...
- 读 Zepto 源码系列
虽然最近工作中没有怎么用 zepto ,但是据说 zepto 的源码比较简单,而且网上的资料也比较多,所以我就挑了 zepto 下手,希望能为以后阅读其他框架的源码打下基础吧. 源码版本 本文阅读的源 ...
- fastclick.js源码解读分析
阅读优秀的js插件和库源码,可以加深我们对web开发的理解和提高js能力,本人能力有限,只能粗略读懂一些小型插件,这里带来对fastclick源码的解读,望各位大神不吝指教~! fastclick诞生 ...
- SDWebImage源码解读之SDWebImageDownloaderOperation
第七篇 前言 本篇文章主要讲解下载操作的相关知识,SDWebImageDownloaderOperation的主要任务是把一张图片从服务器下载到内存中.下载数据并不难,如何对下载这一系列的任务进行设计 ...
- SDWebImage源码解读 之 NSData+ImageContentType
第一篇 前言 从今天开始,我将开启一段源码解读的旅途了.在这里先暂时不透露具体解读的源码到底是哪些?因为也可能随着解读的进行会更改计划.但能够肯定的是,这一系列之中肯定会有Swift版本的代码. 说说 ...
随机推荐
- c语言main函数的参数argc,argv说明
main函数参数argc,argv说明 C/C++语言中的main函数,经常带有参数argc,argv,如下: int main(int argc, char** argv) int main(i ...
- Android 线程优先级
对于Android平台上的线程优先级设置来说可以处理很多并发线程的阻塞问题,比如很多无关紧要的线程会占用大量的CPU时间,虽然通过了MultiThread来解决慢速I/O但是合理分配优先级对于并发编程 ...
- Node.js 创建第一个应用
如果我们使用PHP来编写后端的代码时,需要Apache 或者 Nginx 的HTTP 服务器,并配上 mod_php5 模块和php-cgi. 从这个角度看,整个"接收 HTTP 请求并提供 ...
- java.util.concurrent.Exchanger应用范例与原理浅析--转载
一.简介 Exchanger是自jdk1.5起开始提供的工具套件,一般用于两个工作线程之间交换数据.在本文中我将采取由浅入深的方式来介绍分析这个工具类.首先我们来看看官方的api文档中的叙述: A ...
- .net软件自动化测试笔记(API-2)
1.9获得测试运行时间如何获得测试运行的总时间设计:DateTime.Now属性记录测试开始运行时间,以及测试结束时间,用一个TimeSpan对象计算本次运行的总时间 DateTime starTim ...
- java基础全套
这是我自己早前听课时整理的java基础全套知识 使用于初学者 也可以适用于中级的程序员 我做成了chm文档的类型 你们可以下载 笔记是比较系统全面,可以抵得上市场上90%的学习资料.讨厌那些随便 ...
- NOIP2002 均分纸牌
题一 均分纸牌 (存盘名: NOIPG1) [问题描述] 有 N 堆纸牌,编号分别为 1,2,…, N.每堆上有若干张,但纸牌总数必为 N 的倍数.可以在任一堆上取若于张纸牌,然后移动. 移牌规则为: ...
- globalfifo设备驱动
把globalmem中的全局内存变成一个FIFO,只有当FIFO中有数据的时候(即有进程把数据写到这个FIFO而且没有被读进程读空),读进程才能把数据读出,而且读取后的数据会从globalmem的全局 ...
- Storm系列(一)集群的安装配置
安装前说明: 必须先安装zookeeper集群 该Storm集群由三台机器构成,主机名分别为chenx01,chenx02,chenx03,对应的IP分别为192.168.1.110,192.168. ...
- 64位linux安装android sdk的问题
截至到今天,似乎在64位机器下安装android sdk存在不能运行的问题,可以用以下方法解决: Android SDK requires: Fedora 17 64bit with 32bit An ...