zepto源码注释
zepto源码注释
Zepto 是一个轻量级的 针对现代高级浏览器的JavaScript库, 它与jquery 有着类似的api 。 如果你会用jquery,那么你也会用zepto。这段时间公司的事情比较少,所以就把它的源码看了下,觉得写的挺好的,所以就有了给它写注释的想法。当然,这里面的注释只是我读代码时对它的理解,并不一定正确,如果有错误还请指正,先谢谢了。另外,敬请期待另一个JS大牛(果果)的JS库(then.js)的源码注释。
1 /* Zepto v1.0-1-ga3cab6c - polyfill zepto detect event ajax form fx - zeptojs测试数据/license */
2
3
4 ;( function (undefined){
5 if (String.prototype.trim === undefined) // fix for iOS 3.2
6 String.prototype.trim = function (){ return this .replace(/^\s+|\s+$/g, '' ) }
7
8 // For iOS 3.x
9 // from https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/reduce
10 if (Array.prototype.reduce === undefined)
11 Array.prototype.reduce = function (fun){
12 if ( this === void 0 || this === null ) throw new TypeError()
13 var t = Object( this ), len = t.length >>> 0, k = 0 , accumulator
14 if ( typeof fun != 'function') throw new TypeError()
15 if (len == 0 && arguments.length == 1) throw new TypeError()
16
17 if (arguments.length >= 2 )
18 accumulator = arguments[1 ]
19 else
20 do {
21 if (k in t){
22 accumulator = t[k++ ]
23 break
24 }
25 if (++k >= len) throw new TypeError()
26 } while ( true )
27
28 while (k < len){
29 if (k in t) accumulator = fun.call(undefined, accumulator, t[k], k, t)
30 k++
31 }
32 return accumulator
33 }
34
35 })()
36
37 var Zepto = ( function () {
38 var undefined, key, $, classList, emptyArray = [], slice = emptyArray.slice, filter = emptyArray.filter,
39 document = window.document,
40 elementDisplay = {}, classCache = {},
41 getComputedStyle = document.defaultView.getComputedStyle,
42 // 设置CSS时,不用加px单位的属性
43 cssNumber = { 'column-count': 1, 'columns': 1, 'font-weight': 1, 'line-height': 1,'opacity': 1, 'z-index': 1, 'zoom': 1 },
44 // HTML代码片断的正则
45 fragmentRE = /^\s*<(\w+|!)[^>]*>/ ,
46 // 匹配非单独一个闭合标签的标签,类似将<div></div>写成了<div/>
47 tagExpanderRE = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ ig,
48 // 根节点
49 rootNodeRE = /^(?:body|html)$/ i,
50
51 // 需要提供get和set的方法名
52 // special attributes that should be get/set via method calls
53 methodAttributes = ['val', 'css', 'html', 'text', 'data', 'width', 'height', 'offset' ],
54 // 相邻节点的一些操作
55 adjacencyOperators = [ 'after', 'prepend', 'before', 'append' ],
56 table = document.createElement('table' ),
57 tableRow = document.createElement('tr' ),
58 containers = {
59 'tr': document.createElement('tbody' ),
60 'tbody': table, 'thead': table, 'tfoot' : table,
61 'td': tableRow, 'th' : tableRow,
62 '*': document.createElement('div' )
63 },
64 // 当DOM ready的时候,document会有以下三种状态的一种
65 readyRE = /complete|loaded|interactive/ ,
66 // class选择器的正则
67 classSelectorRE = /^\.([\w-]+)$/ ,
68 // id选择器的正则
69 idSelectorRE = /^#([\w-]*)$/ ,
70 // DOM标签正则
71 tagSelectorRE = /^[\w-]+$/ ,
72 class2type = {},
73 toString = class2type.toString,
74 zepto = {},
75 camelize, uniq,
76 tempParent = document.createElement('div' );
77
78 // 判断一个元素是否匹配给定的选择器
79 zepto.matches = function (element, selector) {
80 if (!element || element.nodeType !== 1) return false
81 // 引用浏览器提供的MatchesSelector方法
82 var matchesSelector = element.webkitMatchesSelector || element.mozMatchesSelector ||
83 element.oMatchesSelector || element.matchesSelector
84 if (matchesSelector) return matchesSelector.call(element, selector);
85 // 如果浏览器不支持MatchesSelector方法,则将节点放入一个临时div节点,
86 // 再通过selector来查找这个div下的节点集,再判断给定的element是否在节点集中,如果在,则返回一个非零(即非false)的数字
87 // fall back to performing a selector:
88 var match, parent = element.parentNode, temp = ! parent
89 // 当element没有父节点,那么将其插入到一个临时的div里面
90 if (temp) (parent = tempParent).appendChild(element)
91 // 将parent作为上下文,来查找selector的匹配结果,并获取element在结果集的索引,不存在时为-1,再通过~-1转成0,存在时返回一个非零的值
92 match = ~ zepto.qsa(parent, selector).indexOf(element)
93 // 将插入的节点删掉
94 temp && tempParent.removeChild(element)
95 return match
96 }
97
98 // 获取对象类型
99 function type(obj) {
100 // obj为null或者undefined时,直接返回'null'或'undefined'
101 return obj == null ? String(obj) : class2type[toString.call(obj)] || "object"
102 }
103
104 function isFunction(value) { return type(value) == "function" }
105 function isWindow(obj) { return obj != null && obj == obj.window }
106 function isDocument(obj) { return obj != null && obj.nodeType == obj.DOCUMENT_NODE }
107 function isObject(obj) { return type(obj) == "object" }
108 // 判断给定的参数是否是由Object构造器生成的对象
109 // 可参考http://snandy.iteye测试数据/blog/663245
110 function isPlainObject(obj) {
111 return isObject(obj) && !isWindow(obj) && obj.__proto__ == Object.prototype
112 }
113 function isArray(value) { return value instanceof Array }
114 // 类数组,比如nodeList,这个只是做最简单的判断,如果给一个对象定义一个值为数据的length属性,它同样会返回true
115 function likeArray(obj) { return typeof obj.length == 'number' }
116
117 // 清除给定的参数中的null或undefined,注意0==null,'' == null为false
118 function compact(array) { return filter.call(array, function (item){ return item != null }) }
119 // 类似得到一个数组的副本
120 function flatten(array) { return array.length > 0 ? $.fn.concat.apply([], array) : array }
121 // 将字符串转成驼峰式的格式
122 camelize = function (str){ return str.replace(/-+(.)?/g, function (match, chr){ return chr ? chr.toUpperCase() : '' }) }
123 // 将字符串格式化成-拼接的形式,一般用在样式属性上,比如border-width
124 function dasherize(str) {
125 return str.replace(/::/g, '/') // 将::替换成/
126 .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') // 在大小写字符之间插入_,大写在前,比如AAAbb,得到AA_Abb
127 .replace(/([a-z\d])([A-Z])/g, '$1_$2') // 在大小写字符之间插入_,小写或数字在前,比如bbbAaa,得到bbb_Aaa
128 .replace(/_/g, '-') // 将_替换成-
129 .toLowerCase() // 转成小写
130 }
131 // 数组去重
132 uniq = function (array){ return filter.call(array, function (item, idx){ return array.indexOf(item) == idx }) }
133
134 // 将给定的参数生成正则
135 function classRE(name) {
136 // classCache,缓存正则
137 return name in classCache ?
138 classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)' ))
139 }
140 // 给需要的样式值后面加上'px'单位,除了cssNumber里面的指定的那些
141 function maybeAddPx(name, value) {
142 return ( typeof value == "number" && !cssNumber[dasherize(name)]) ? value + "px" : value
143 }
144 // 获取节点的默认display属性
145 function defaultDisplay(nodeName) {
146 var element, display
147 if (!elementDisplay[nodeName]) { // 缓存里不存在
148 element = document.createElement(nodeName)
149 document.body.appendChild(element)
150 display = getComputedStyle(element, '').getPropertyValue("display" )
151 element.parentNode.removeChild(element)
152 display == "none" && (display = "block") // 当display等于none时,设置其值为block,搞不懂为毛要这样
153 elementDisplay[nodeName] = display // 缓存元素的默认display属性
154 }
155 return elementDisplay[nodeName]
156 }
157 // 获取指定元素的子节点(不包含文本节点),Firefox不支持children,所以只能通过筛选childNodes
158 function children(element) {
159 return 'children' in element ?
160 slice.call(element.children) :
161 $.map(element.childNodes, function (node){ if (node.nodeType == 1) return node })
162 }
163
164 // `$.zepto.fragment` takes a html string and an optional tag name
165 // to generate DOM nodes nodes from the given html string.
166 // The generated DOM nodes are returned as an array.
167 // This function can be overriden in plugins for example to make
168 // it compatible with browsers that don't support the DOM fully.
169 zepto.fragment = function (html, name, properties) {
170 // 将类似<div class="test"/>替换成<div class="test"></div>,算是一种修复吧
171 if (html.replace) html = html.replace(tagExpanderRE, "<$1></$2>" )
172 // 当不指定name的时候,给name取标签名
173 if (name === undefined) name = fragmentRE.test(html) && RegExp.$1
174 // 当name不等于tr,tbody,thead,tfoot,th,td时,赋值*
175 if (!(name in containers)) name = '*'
176
177 var nodes, dom, container = containers[name] // 创建容器
178 container.innerHTML = '' + html // 将html代码片断放入容器
179 // 取容器的子节点,这样就直接把字符串转成DOM节点了
180 dom = $.each(slice.call(container.childNodes), function (){
181 container.removeChild( this ) // 逐个删除
182 })
183 // 如果properties是对象
184 if (isPlainObject(properties)) {
185 nodes = $(dom) // 将dom转成zepto对象,为了方便下面调用zepto上的方法
186 // 遍历对象,设置属性
187 $.each(properties, function (key, value) {
188 // 如果设置的是'val', 'css', 'html', 'text', 'data', 'width', 'height', 'offset',则调用zepto上相对应的方法
189 if (methodAttributes.indexOf(key) > -1 ) nodes[key](value)
190 else nodes.attr(key, value)
191 })
192 }
193 return dom
194 }
195
196 // `$.zepto.Z` swaps out the prototype of the given `dom` array
197 // of nodes with `$.fn` and thus supplying all the Zepto functions
198 // to the array. Note that `__proto__` is not supported on Internet
199 // Explorer. This method can be overriden in plugins.
200 zepto.Z = function (dom, selector) {
201 dom = dom || []
202 dom.__proto__ = $.fn // 通过给dom设置__proto__属性指向$.fn来达到继承$.fn上所有方法的目的
203 dom.selector = selector || ''
204 return dom
205 }
206
207 // `$.zepto.isZ` should return `true` if the given object is a Zepto
208 // collection. This method can be overriden in plugins.
209 // 判断给定的参数是否是Zepto集
210 zepto.isZ = function (object) {
211 return object instanceof zepto.Z
212 }
213
214 // `$.zepto.init` is Zepto's counterpart to jQuery's `$.fn.init` and
215 // takes a CSS selector and an optional context (and handles various
216 // special cases).
217 // This method can be overriden in plugins.
218 zepto.init = function (selector, context) {
219 // If nothing given, return an empty Zepto collection
220 if (!selector) return zepto.Z() // 没有参数,返回空数组
221 // If a function is given, call it when the DOM is ready
222 else if (isFunction(selector)) return $(document).ready(selector)
223 // If a Zepto collection is given, juts return it
224 else if (zepto.isZ(selector)) return selector
225 else {
226 var dom
227 // normalize array if an array of nodes is given
228 if (isArray(selector)) dom = compact(selector)
229 // Wrap DOM nodes. If a plain object is given, duplicate it.
230 else if (isObject(selector))
231 dom = [isPlainObject(selector) ? $.extend({}, selector) : selector], selector = null
232 // If it's a html fragment, create nodes from it
233 else if (fragmentRE.test(selector))
234 dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null
235 // If there's a context, create a collection on that context first, and select
236 // nodes from there
237 else if (context !== undefined) return $(context).find(selector)
238 // And last but no least, if it's a CSS selector, use it to select nodes.
239 else dom = zepto.qsa(document, selector)
240 // create a new Zepto collection from the nodes found
241 return zepto.Z(dom, selector)
242 }
243 }
244
245 // `$` will be the base `Zepto` object. When calling this
246 // function just call `$.zepto.init, which makes the implementation
247 // details of selecting nodes and creating Zepto collections
248 // patchable in plugins.
249 $ = function (selector, context){
250 return zepto.init(selector, context)
251 }
252
253 // 扩展,deep表示是否深度扩展
254 function extend(target, source, deep) {
255 for (key in source)
256 // 如果深度扩展
257 if (deep && (isPlainObject(source[key]) || isArray(source[key]))) {
258 // 如果要扩展的数据是对象且target相对应的key不是对象
259 if (isPlainObject(source[key]) && ! isPlainObject(target[key]))
260 target[key] = {}
261 // 如果要扩展的数据是数组且target相对应的key不是数组
262 if (isArray(source[key]) && ! isArray(target[key]))
263 target[key] = []
264 extend(target[key], source[key], deep)
265 }
266 else if (source[key] !== undefined) target[key] = source[key]
267 }
268
269 // Copy all but undefined properties from one or more
270 // objects to the `target` object.
271 $.extend = function (target){
272 var deep, args = slice.call(arguments, 1 )
273 if ( typeof target == 'boolean') { // 当第一个参数为boolean类型的值时,表示是否深度扩展
274 deep = target
275 target = args.shift() // target取第二个参数
276 }
277 // 遍历后面的参数,全部扩展到target上
278 args.forEach( function (arg){ extend(target, arg, deep) })
279 return target
280 }
281
282 // `$.zepto.qsa` is Zepto's CSS selector implementation which
283 // uses `document.querySelectorAll` and optimizes for some special cases, like `#id`.
284 // This method can be overriden in plugins.
285 zepto.qsa = function (element, selector){
286 var found
287 // 当element为document,且selector为ID选择器时
288 return (isDocument(element) && idSelectorRE.test(selector)) ?
289 // 直接返回document.getElementById,RegExp.$1为ID的值,当没有找节点时返回[]
290 ( (found = element.getElementById(RegExp.$1)) ? [found] : [] ) :
291 // 当element不为元素节点或者document时,返回[]
292 (element.nodeType !== 1 && element.nodeType !== 9) ? [] :
293 // 否则将获取到的结果转成数组并返回
294 slice.call(
295 // 如果selector是标签名,直接调用getElementsByClassName
296 classSelectorRE.test(selector) ? element.getElementsByClassName(RegExp.$1 ) :
297 // 如果selector是标签名,直接调用getElementsByTagName
298 tagSelectorRE.test(selector) ? element.getElementsByTagName(selector) :
299 // 否则调用querySelectorAll
300 element.querySelectorAll(selector)
301 )
302 }
303
304 // 在结果中进行过滤
305 function filtered(nodes, selector) {
306 return selector === undefined ? $(nodes) : $(nodes).filter(selector)
307 }
308 // 判断parent是否包含node
309 $.contains = function (parent, node) {
310 return parent !== node && parent.contains(node)
311 }
312
313 function funcArg(context, arg, idx, payload) {
314 return isFunction(arg) ? arg.call(context, idx, payload) : arg
315 }
316
317 function setAttribute(node, name, value) {
318 // 如果设置的值为null或undefined,则相当于删除该属性,否则设置name属性为value
319 value == null ? node.removeAttribute(name) : node.setAttribute(name, value)
320 }
321
322 // access className property while respecting SVGAnimatedString
323 function className(node, value){
324 var klass = node.className,
325 svg = klass && klass.baseVal !== undefined
326
327 if (value === undefined) return svg ? klass.baseVal : klass
328 svg ? (klass.baseVal = value) : (node.className = value)
329 }
330
331 // "true" => true
332 // "false" => false
333 // "null" => null
334 // "42" => 42
335 // "42.5" => 42.5
336 // JSON => parse if valid
337 // String => self
338 function deserializeValue(value) {
339 var num
340 try {
341 return value ?
342 value == "true" ||
343 ( value == "false" ? false :
344 value == "null" ? null :
345 !isNaN(num = Number(value)) ? num :
346 /^[\[\{]/.test(value) ? $.parseJSON(value) :
347 value )
348 : value
349 } catch (e) {
350 return value
351 }
352 }
353
354 $.type = type
355 $.isFunction = isFunction
356 $.isWindow = isWindow
357 $.isArray = isArray
358 $.isPlainObject = isPlainObject
359
360 // 空对象
361 $.isEmptyObject = function (obj) {
362 var name
363 for (name in obj) return false
364 return true
365 }
366
367 // 获取指定的值在数组中的位置
368 $.inArray = function (elem, array, i){
369 return emptyArray.indexOf.call(array, elem, i)
370 }
371 // 将字符串转成驼峰式的格式
372 $.camelCase = camelize
373 // 去字符串头尾空格
374 $.trim = function (str) { return str.trim() }
375
376 // plugin compatibility
377 $.uuid = 0
378 $.support = { }
379 $.expr = { }
380
381 // 遍历elements,将每次放入callback里处理,保存处理结果不为null的项
382 $.map = function (elements, callback){
383 var value, values = [], i, key
384 if (likeArray(elements))
385 for (i = 0; i < elements.length; i++ ) {
386 value = callback(elements[i], i)
387 if (value != null ) values.push(value)
388 }
389 else
390 for (key in elements) {
391 value = callback(elements[key], key)
392 if (value != null ) values.push(value)
393 }
394 return flatten(values)
395 }
396
397 $.each = function (elements, callback){
398 var i, key
399 if (likeArray(elements)) {
400 for (i = 0; i < elements.length; i++ )
401 if (callback.call(elements[i], i, elements[i]) === false ) return elements
402 } else {
403 for (key in elements)
404 if (callback.call(elements[key], key, elements[key]) === false ) return elements
405 }
406
407 return elements
408 }
409 // 过滤
410 $.grep = function (elements, callback){
411 return filter.call(elements, callback)
412 }
413
414 if (window.JSON) $.parseJSON = JSON.parse
415
416 // Populate the class2type map
417 // 填充class2type的值
418 $.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function (i, name) {
419 class2type[ "[object " + name + "]" ] = name.toLowerCase()
420 })
421
422 // 针对DOM的一些操作
423 // Define methods that will be available on all
424 // Zepto collections
425 $.fn = {
426 // Because a collection acts like an array
427 // copy over these useful array functions.
428 forEach: emptyArray.forEach,
429 reduce: emptyArray.reduce,
430 push: emptyArray.push,
431 sort: emptyArray.sort,
432 indexOf: emptyArray.indexOf,
433 concat: emptyArray.concat,
434
435 // `map` and `slice` in the jQuery API work differently
436 // from their array counterparts
437 map: function (fn){
438 return $($.map( this , function (el, i){ return fn.call(el, i, el) }))
439 },
440 slice: function (){
441 return $(slice.apply( this , arguments))
442 },
443 // DOM Ready
444 ready: function (callback){
445 if (readyRE.test(document.readyState)) callback($)
446 else document.addEventListener('DOMContentLoaded', function (){ callback($) }, false )
447 return this
448 },
449 // 取集合中对应指定索引的值,如果idx小于0,则idx等于idx+length,length为集合的长度
450 get: function (idx){
451 return idx === undefined ? slice.call( this ) : this [idx >= 0 ? idx : idx + this .length]
452 },
453 // 将集合转换为数组
454 toArray: function (){ return this .get() },
455 // 获取集合长度
456 size: function (){
457 return this .length
458 },
459 // 将集合从dom中删除
460 remove: function (){
461 return this .each( function (){
462 if ( this .parentNode != null )
463 this .parentNode.removeChild( this )
464 })
465 },
466 // 遍历集合,将集合中的每一项放入callback中进行处理,去掉结果为false的项
467 each: function (callback){
468 emptyArray.every.call( this , function (el, idx){
469 return callback.call(el, idx, el) !== false
470 })
471 return this
472 },
473 // 过滤集合,返回处理结果为true的记录
474 filter: function (selector){
475 // this.not(selector)取到需要排除的集合,第二次再取反(这个时候this.not的参数就是一个集合了),得到想要的集合
476 if (isFunction(selector)) return this .not( this .not(selector))
477 // filter收集返回结果为true的记录
478 return $(filter.call( this , function (element){
479 return zepto.matches(element, selector) // 当element与selector匹配,则收集
480 }))
481 },
482 // 将由selector获取到的结果追加到当前集合中
483 add: function (selector,context){
484 return $(uniq( this .concat($(selector,context)))) // 追求并去重
485 },
486 // 返回集合中的第1条记录是否与selector匹配
487 is: function (selector){
488 return this .length > 0 && zepto.matches( this [0 ], selector)
489 },
490 // 排除集合里满足条件的记录,接收参数为:字符串选择器,function, DOM,DOM集合
491 not: function (selector){
492 var nodes= []
493 // 当selector为函数时
494 if (isFunction(selector) && selector.call !== undefined){
495 this .each( function (idx){
496 // 注意这里收集的是selector.call(this,idx)返回结果为false的时候记录
497 if (!selector.call( this ,idx)) nodes.push( this )
498 })
499 } else {
500 // 当selector为字符串的时候,对集合进行筛选,也就是筛选出集合中满足selector的记录
501 var excludes = typeof selector == 'string' ? this .filter(selector) :
502 // 当selector为数组时执行slice.call(selector),当selector为节点时,执行$(selector)
503 (likeArray(selector) && isFunction(selector.item)) ? slice.call(selector) : $(selector)
504 this .forEach( function (el){
505 // 筛选出不在excludes集合里的记当,达到排除的目的
506 if (excludes.indexOf(el) < 0 ) nodes.push(el)
507 })
508 }
509 return $(nodes)
510 },
511 /*
512 接收node和string作为参数,给当前集合筛选出包含selector的集合
513 isObject(selector)是判断参数是否是node,因为typeof node == 'object'
514 当参数为node时,只需要判读当前记当里是否包含node节点即可
515 当参数为string时,则在当前记录里查询selector,如果长度为0,则为false,filter函数就会过滤掉这条记录,否则保存该记录
516 */
517 has: function (selector){
518 return this .filter( function (){
519 return isObject(selector) ? $.contains( this , selector) : $( this ).find(selector).size()
520 })
521 },
522 /*
523 选择集合中指定索引的记录,当idx为-1时,取最后一个记录
524 */
525 eq: function (idx){
526 return idx === -1 ? this .slice(idx) : this .slice(idx, + idx + 1 )
527 },
528 /*
529 取集合中的第一条记录
530 */
531 first: function (){
532 var el = this [0] // 取集合中的第一条记录
533 // 如果el为node,则isObject(el)会为true,需要转成zepto对象
534 return el && !isObject(el) ? el : $(el)
535 },
536 /*
537 取集合中的最后一条记录
538 */
539 last: function (){
540 var el = this [ this .length - 1] // 取集合中的最后一条记录
541 // 如果el为node,则isObject(el)会为true,需要转成zepto对象
542 return el && !isObject(el) ? el : $(el)
543 },
544 /*
545 在当前集合中查找selector,selector可以是集合,选择器,以及节点
546 */
547 find: function (selector){
548 var result, $ this = this
549 // 如果selector为node或者zepto集合时
550 if ( typeof selector == 'object' )
551 // 遍历selector,筛选出父级为集合中记录的selector
552 result = $(selector).filter( function (){
553 var node = this
554 // 如果$.contains(parent, node)返回true,则emptyArray.some也会返回true,外层的filter则会收录该条记录
555 return emptyArray.some.call($ this , function (parent){
556 return $.contains(parent, node)
557 })
558 })
559 // 如果当前集合长度为1时,调用zepto.qsa,将结果转成zepto对象
560 else if ( this .length == 1) result = $(zepto.qsa( this [0 ], selector))
561 // 如果长度大于1,则调用map遍历
562 else result = this .map( function (){ return zepto.qsa( this , selector) })
563 return result
564 },
565 // 取集合中第一记录的最近的父级元素
566 closest: function (selector, context){
567 var node = this [0], collection = false
568 if ( typeof selector == 'object') collection = $(selector)
569 // 当selector是node或者zepto集合时,如果node不在collection集合中时需要取node.parentNode进行判断
570 // 当selector是字符串选择器时,如果node与selector不匹配,则需要取node.parentNode进行判断
571 while (node && !(collection ? collection.indexOf(node) >= 0 : zepto.matches(node, selector)))
572 // 当node 不是context,document的时候,取node.parentNode
573 node = node !== context && !isDocument(node) && node.parentNode
574 return $(node)
575 },
576 // 取集合所有父级元素
577 parents: function (selector){
578 var ancestors = [], nodes = this
579 // 通过遍历nodes得到所有父级,注意在while里nodes被重新赋值了
580 // 本函数的巧妙之处在于,不停在获取父级,再遍历父级获取父级的父级
581 // 然后再通过去重,得到最终想要的结果,当到达最顶层的父级时,nodes.length就为0了
582 while (nodes.length > 0 )
583 // nodes被重新赋值为收集到的父级集合
584 nodes = $.map(nodes, function (node){
585 // 遍历nodes,收集集合的第一层父级
586 // ancestors.indexOf(node) < 0用来去重复
587 if ((node = node.parentNode) && !isDocument(node) && ancestors.indexOf(node) < 0 ) {
588 ancestors.push(node) // 收集已经获取到的父级元素
589 return node
590 }
591 })
592 // 上面还只是取到了所有的父级元素,这里还需要对其进行筛选从而得到最终想要的结果
593 return filtered(ancestors, selector)
594 },
595 // 获取集合的父节点
596 parent: function (selector){
597 return filtered(uniq( this .pluck('parentNode' )), selector)
598 },
599 children: function (selector){
600 return filtered( this .map( function (){ return children( this ) }), selector)
601 },
602 contents: function () {
603 return this .map( function () { return slice.call( this .childNodes) })
604 },
605 siblings: function (selector){
606 return filtered( this .map( function (i, el){
607 // 先获取该节点的父节点中的所有子节点,再排除本身
608 return filter.call(children(el.parentNode), function (child){ return child!== el })
609 }), selector)
610 },
611 empty: function (){
612 return this .each( function (){ this .innerHTML = '' })
613 },
614 // 根据属性来获取当前集合的相关集合
615 pluck: function (property){
616 return $.map( this , function (el){ return el[property] })
617 },
618 show: function (){
619 return this .each( function (){
620 // 清除元素的内联display="none"的样式
621 this .style.display == "none" && ( this .style.display = null )
622 // 当样式表里的该元素的display样式为none时,设置它的display为默认值
623 if (getComputedStyle( this , '').getPropertyValue("display") == "none" )
624 this .style.display = defaultDisplay( this .nodeName) // defaultDisplay是获取元素默认display的方法
625 })
626 },
627 replaceWith: function (newContent){
628 // 将要替换的内容插入到被替换的内容前面,然后删除被替换的内容
629 return this .before(newContent).remove()
630 },
631 wrap: function (structure){
632 var func = isFunction(structure)
633 if ( this [0] && ! func)
634 // 如果structure是字符串,则将其转成DOM
635 var dom = $(structure).get(0 ),
636 // 如果structure是已经存在于页面上的节点或者被wrap的记录不只一条,则需要clone dom
637 clone = dom.parentNode || this .length > 1
638
639 return this .each( function (index){
640 $( this ).wrapAll(
641 func ? structure.call( this , index) :
642 clone ? dom.cloneNode( true ) : dom
643 )
644 })
645 },
646 wrapAll: function (structure){
647 if ( this [0 ]) {
648 // 将要包裹的内容插入到第一条记录的前面,算是给structure定位围置
649 $( this [0]).before(structure = $(structure))
650 var children
651 // drill down to the inmost element
652 // 取structure里的第一个子节点的最里层
653 while ((children = structure.children()).length) structure = children.first()
654 // 将当前集合插入到最里层的节点里,达到wrapAll的目的
655 $(structure).append( this )
656 }
657 return this
658 },
659 // 在匹配元素里的内容外包一层结构
660 wrapInner: function (structure){
661 var func = isFunction(structure)
662 return this .each( function (index){
663 // 原理就是获取节点的内容,然后将structure将内容包起来,如果内容不存在,则直接将structure append到该节点
664 var self = $( this ), contents = self.contents(),
665 dom = func ? structure.call( this , index) : structure
666 contents.length ? contents.wrapAll(dom) : self.append(dom)
667 })
668 },
669 unwrap: function (){
670 // 用子元素替换掉父级
671 this .parent().each( function (){
672 $( this ).replaceWith($( this ).children())
673 })
674 return this
675 },
676 // clone node
677 clone: function (){
678 return this .map( function (){ return this .cloneNode( true ) })
679 },
680 // 隐藏集合
681 hide: function (){
682 return this .css("display", "none" )
683 },
684 toggle: function (setting){
685 return this .each( function (){
686 var el = $( this )
687 /*
688 这个setting取得作用就是控制显示与隐藏,并不切换,当它的值为true时,一直显示,false时,一直隐藏
689 这个地方的判断看上去有点绕,其实也简单,意思是说,当不给toogle参数时,根据元素的display是否等于none来决定显示或者隐藏元素
690 当给toogle参数,就没有切换效果了,只是简单的根据参数值来决定显示或隐藏。如果参数true,相当于show方法,false则相当于hide方法
691 */
692 ;(setting === undefined ? el.css("display") == "none" : setting) ? el.show() : el.hide()
693 })
694 },
695 prev: function (selector){ return $( this .pluck('previousElementSibling')).filter(selector || '*' ) },
696 next: function (selector){ return $( this .pluck('nextElementSibling')).filter(selector || '*' ) },
697 // 当有参数时,设置集合每条记录的HTML,没有参数时,则为获取集合第一条记录的HTML,如果集合的长度为0,则返回null
698 html: function (html){
699 return html === undefined ?
700 // 参数html不存在时,获取集合中第一条记录的html
701 ( this .length > 0 ? this [0].innerHTML : null ) :
702 // 否则遍历集合,设置每条记录的html
703 this .each( function (idx){
704 // 记录原始的innerHTMl
705 var originHtml = this .innerHTML
706 // 如果参数html是字符串直接插入到记录中,
707 // 如果是函数,则将当前记录作为上下文,调用该函数,且传入该记录的索引和原始innerHTML作为参数
708 $( this ).empty().append( funcArg( this , html, idx, originHtml) )
709 })
710 },
711 text: function (text){
712 return text === undefined ?
713 ( this .length > 0 ? this [0].textContent : null ) :
714 this .each( function (){ this .textContent = text })
715 },
716 attr: function (name, value){
717 var result
718 // 当只有name且为字符串时,表示获取第一条记录的属性
719 return ( typeof name == 'string' && value === undefined) ?
720 // 集合没有记录或者集合的元素不是node类型,返回undefined
721 ( this .length == 0 || this [0].nodeType !== 1 ? undefined :
722 // 如果取的是input的value
723 (name == 'value' && this [0].nodeName == 'INPUT') ? this .val() :
724 // 注意直接定义在node上的属性,在标准浏览器和ie9,10中用getAttribute取不到,得到的结果是null
725 // 比如div.aa = 10,用div.getAttribute('aa')得到的是null,需要用div.aa或者div['aa']这样来取
726 (!(result = this [0].getAttribute(name)) && name in this [0]) ? this [0 ][name] : result
727 ) :
728 // 有两个参数时为设置
729 this .each( function (idx){
730 if ( this .nodeType !== 1) return
731 if (isObject(name)) for (key in name) setAttribute( this , key, name[key])
732 else setAttribute( this , name, funcArg( this , value, idx, this .getAttribute(name)))
733 })
734 },
735 removeAttr: function (name){
736 return this .each( function (){ this .nodeType === 1 && setAttribute( this , name) })
737 },
738 prop: function (name, value){
739 return (value === undefined) ?
740 ( this [0] && this [0 ][name]) :
741 this .each( function (idx){
742 this [name] = funcArg( this , value, idx, this [name])
743 })
744 },
745 data: function (name, value){
746 var data = this .attr('data-' + dasherize(name), value)
747 return data !== null ? deserializeValue(data) : undefined
748 },
749 val: function (value){
750 return (value === undefined) ?
751 // 如果是多选的select,则返回一个包含被选中的option的值的数组
752 ( this [0] && ( this [0].multiple ?
753 $( this [0]).find('option').filter( function (o){ return this .selected }).pluck('value' ) :
754 this [0 ].value)
755 ) :
756 this .each( function (idx){
757 this .value = funcArg( this , value, idx, this .value)
758 })
759 },
760 offset: function (coordinates){
761 if (coordinates) return this .each( function (index){
762 var $ this = $( this ),
763 // coordinates为{}时直接返回,为函数时返回处理结果给coords
764 coords = funcArg( this , coordinates, index, $ this .offset()),
765 // 取父级的offset
766 parentOffset = $ this .offsetParent().offset(),
767 // 计算出它们之间的差,得出其偏移量
768 props = {
769 top: coords.top - parentOffset.top,
770 left: coords.left - parentOffset.left
771 }
772 // 注意元素的position为static时,设置top,left是无效的
773 if ($ this .css('position') == 'static') props['position'] = 'relative'
774 $ this .css(props)
775 })
776 // 取第一条记录的offset,包括offsetTop,offsetLeft,offsetWidth,offsetHeight
777 if ( this .length==0) return null
778 var obj = this [0 ].getBoundingClientRect()
779 // window.pageYOffset就是类似Math.max(document.documentElement.scrollTop||document.body.scrollTop)
780 return {
781 left: obj.left + window.pageXOffset,
782 top: obj.top + window.pageYOffset,
783 Math.round(obj.width),
784 height: Math.round(obj.height)
785 }
786 },
787 css: function (property, value){
788 // 获取指定的样式
789 if (arguments.length < 2 && typeof property == 'string' )
790 return this [0] && ( this [0].style[camelize(property)] || getComputedStyle( this [0], '' ).getPropertyValue(property))
791 // 设置样式
792 var css = ''
793 if (type(property) == 'string' ) {
794 if (!value && value !== 0) // 当value的值为非零的可以转成false的值时,删掉property样式
795 this .each( function (){ this .style.removeProperty(dasherize(property)) })
796 else
797 css = dasherize(property) + ":" + maybeAddPx(property, value)
798 } else {
799 // 当property是对象时
800 for (key in property)
801 if (!property[key] && property[key] !== 0 )
802 // 当property[key]的值为非零的可以转成false的值时,删掉key样式
803 this .each( function (){ this .style.removeProperty(dasherize(key)) })
804 else
805 css += dasherize(key) + ':' + maybeAddPx(key, property[key]) + ';'
806 }
807 // 设置
808 return this .each( function (){ this .style.cssText += ';' + css })
809 },
810 index: function (element){
811 // 这里的$(element)[0]是为了将字符串转成node,因为this是个包含node的数组
812 // 当不指定element时,取集合中第一条记录在其父节点的位置
813 // this.parent().children().indexOf(this[0])这句很巧妙,和取第一记录的parent().children().indexOf(this)相同
814 return element ? this .indexOf($(element)[0]) : this .parent().children().indexOf( this [0 ])
815 },
816 hasClass: function (name){
817 return emptyArray.some.call( this , function (el){
818 // 注意这里的this是classRE(name)生成的正则
819 return this .test(className(el))
820 }, classRE(name))
821 },
822 addClass: function (name){
823 return this .each( function (idx){
824 classList = []
825 var cls = className( this ), newName = funcArg( this , name, idx, cls)
826 // 处理同时多个类的情况,用空格分开
827 newName.split(/\s+/g).forEach( function (klass){
828 if (!$( this ).hasClass(klass)) classList.push(klass)
829 }, this )
830 classList.length && className( this , cls + (cls ? " " : "") + classList.join(" " ))
831 })
832 },
833 removeClass: function (name){
834 return this .each( function (idx){
835 if (name === undefined) return className( this , '' )
836 classList = className( this )
837 funcArg( this , name, idx, classList).split(/\s+/g).forEach( function (klass){
838 classList = classList.replace(classRE(klass), " " )
839 })
840 className( this , classList.trim())
841 })
842 },
843 toggleClass: function (name, when){
844 return this .each( function (idx){
845 var $ this = $( this ), names = funcArg( this , name, idx, className( this ))
846 names.split(/\s+/g).forEach( function (klass){
847 (when === undefined ? !$ this .hasClass(klass) : when) ?
848 $ this .addClass(klass) : $ this .removeClass(klass)
849 })
850 })
851 },
852 scrollTop: function (){
853 if (! this .length) return
854 return ('scrollTop' in this [0]) ? this [0].scrollTop : this [0 ].scrollY
855 },
856 position: function () {
857 if (! this .length) return
858
859 var elem = this [0 ],
860 // Get *real* offsetParent
861 offsetParent = this .offsetParent(),
862 // Get correct offsets
863 offset = this .offset(),
864 parentOffset = rootNodeRE.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset()
865
866 // Subtract element margins
867 // note: when an element has margin: auto the offsetLeft and marginLeft
868 // are the same in Safari causing offset.left to incorrectly be 0
869 offset.top -= parseFloat( $(elem).css('margin-top') ) || 0
870 offset.left -= parseFloat( $(elem).css('margin-left') ) || 0
871
872 // Add offsetParent borders
873 parentOffset.top += parseFloat( $(offsetParent[0]).css('border-top-width') ) || 0
874 parentOffset.left += parseFloat( $(offsetParent[0]).css('border-left-width') ) || 0
875
876 // Subtract the two offsets
877 return {
878 top: offset.top - parentOffset.top,
879 left: offset.left - parentOffset.left
880 }
881 },
882 offsetParent: function () {
883 return this .map( function (){
884 var parent = this .offsetParent || document.body
885 while (parent && !rootNodeRE.test(parent.nodeName) && $(parent).css("position") == "static" )
886 parent = parent.offsetParent
887 return parent
888 })
889 }
890 }
891
892 // for now
893 $.fn.detach = $.fn.remove
894
895 // Generate the `width` and `height` functions
896 ;['width', 'height'].forEach( function (dimension){
897 $.fn[dimension] = function (value){
898 var offset, el = this [0 ],
899 // 将width,hegiht转成Width,Height,用于取window或者document的width和height
900 Dimension = dimension.replace(/./, function (m){ return m[0 ].toUpperCase() })
901 // 没有参数为获取,获取window的width和height用innerWidth,innerHeight
902 if (value === undefined) return isWindow(el) ? el['inner' + Dimension] :
903 // 获取document的width和height时,用offsetWidth,offsetHeight
904 isDocument(el) ? el.documentElement['offset' + Dimension] :
905 (offset = this .offset()) && offset[dimension]
906 else return this .each( function (idx){
907 el = $( this )
908 el.css(dimension, funcArg( this , value, idx, el[dimension]()))
909 })
910 }
911 })
912
913 function traverseNode(node, fun) {
914 fun(node)
915 for ( var key in node.childNodes) traverseNode(node.childNodes[key], fun)
916 }
917
918 // Generate the `after`, `prepend`, `before`, `append`,
919 // `insertAfter`, `insertBefore`, `appendTo`, and `prependTo` methods.
920 adjacencyOperators.forEach( function (operator, operatorIndex) {
921 var inside = operatorIndex % 2 // => prepend, append
922
923 $.fn[operator] = function (){
924 // arguments can be nodes, arrays of nodes, Zepto objects and HTML strings
925 var argType, nodes = $.map(arguments, function (arg) {
926 argType = type(arg)
927 return argType == "object" || argType == "array" || arg == null ?
928 arg : zepto.fragment(arg)
929 }),
930 parent, copyByClone = this .length > 1 // 如果集合的长度大于集,则需要clone被插入的节点
931 if (nodes.length < 1) return this
932
933 return this .each( function (_, target){
934 parent = inside ? target : target.parentNode
935
936 // 通过改变target将after,prepend,append操作转成before操作,insertBefore的第二个参数为null时等于appendChild操作
937 target = operatorIndex == 0 ? target.nextSibling :
938 operatorIndex == 1 ? target.firstChild :
939 operatorIndex == 2 ? target :
940 null
941
942 nodes.forEach( function (node){
943 if (copyByClone) node = node.cloneNode( true )
944 else if (!parent) return $(node).remove()
945
946 // 插入节点后,如果被插入的节点是SCRIPT,则执行里面的内容并将window设为上下文
947 traverseNode(parent.insertBefore(node, target), function (el){
948 if (el.nodeName != null && el.nodeName.toUpperCase() === 'SCRIPT' &&
949 (!el.type || el.type === 'text/javascript') && ! el.src)
950 window['eval' ].call(window, el.innerHTML)
951 })
952 })
953 })
954 }
955
956 // after => insertAfter
957 // prepend => prependTo
958 // before => insertBefore
959 // append => appendTo
960 $.fn[inside ? operator+'To' : 'insert'+(operatorIndex ? 'Before' : 'After')] = function (html){
961 $(html)[operator]( this )
962 return this
963 }
964 })
965
966 zepto.Z.prototype = $.fn
967
968 // Export internal API functions in the `$.zepto` namespace
969 zepto.uniq = uniq
970 zepto.deserializeValue = deserializeValue
971 $.zepto = zepto
972
973 return $
974 })()
975
976 window.Zepto = Zepto
977 '$' in window || (window.$ = Zepto)
978
979 ;( function ($){
980 function detect(ua){
981 var os = this .os = {}, browser = this .browser = {},
982 webkit = ua.match(/WebKit\/([\d.]+)/ ),
983 android = ua.match(/(Android)\s+([\d.]+)/ ),
984 ipad = ua.match(/(iPad).*OS\s([\d_]+)/ ),
985 iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/ ),
986 webos = ua.match(/(webOS|hpwOS)[\s\/]([\d.]+)/ ),
987 touchpad = webos && ua.match(/TouchPad/ ),
988 kindle = ua.match(/Kindle\/([\d.]+)/ ),
989 silk = ua.match(/Silk\/([\d._]+)/ ),
990 blackberry = ua.match(/(BlackBerry).*Version\/([\d.]+)/ ),
991 bb10 = ua.match(/(BB10).*Version\/([\d.]+)/ ),
992 rimtabletos = ua.match(/(RIM\sTablet\sOS)\s([\d.]+)/ ),
993 playbook = ua.match(/PlayBook/ ),
994 chrome = ua.match(/Chrome\/([\d.]+)/) || ua.match(/CriOS\/([\d.]+)/ ),
995 firefox = ua.match(/Firefox\/([\d.]+)/ )
996
997 // Todo: clean this up with a better OS/browser seperation:
998 // - discern (more) between multiple browsers on android
999 // - decide if kindle fire in silk mode is android or not
1000 // - Firefox on Android doesn't specify the Android version
1001 // - possibly devide in os, device and browser hashes
1002
1003 if (browser.webkit = !!webkit) browser.version = webkit[1 ]
1004
1005 if (android) os.android = true , os.version = android[2 ]
1006 if (iphone) os.ios = os.iphone = true , os.version = iphone[2].replace(/_/g, '.' )
1007 if (ipad) os.ios = os.ipad = true , os.version = ipad[2].replace(/_/g, '.' )
1008 if (webos) os.webos = true , os.version = webos[2 ]
1009 if (touchpad) os.touchpad = true
1010 if (blackberry) os.blackberry = true , os.version = blackberry[2 ]
1011 if (bb10) os.bb10 = true , os.version = bb10[2 ]
1012 if (rimtabletos) os.rimtabletos = true , os.version = rimtabletos[2 ]
1013 if (playbook) browser.playbook = true
1014 if (kindle) os.kindle = true , os.version = kindle[1 ]
1015 if (silk) browser.silk = true , browser.version = silk[1 ]
1016 if (!silk && os.android && ua.match(/Kindle Fire/)) browser.silk = true
1017 if (chrome) browser.chrome = true , browser.version = chrome[1 ]
1018 if (firefox) browser.firefox = true , browser.version = firefox[1 ]
1019
1020 os.tablet = !!(ipad || playbook || (android && !ua.match(/Mobile/)) || (firefox && ua.match(/Tablet/ )))
1021 os.phone = !!(!os.tablet && (android || iphone || webos || blackberry || bb10 ||
1022 (chrome && ua.match(/Android/)) || (chrome && ua.match(/CriOS\/([\d.]+)/)) || (firefox && ua.match(/Mobile/ ))))
1023 }
1024
1025 detect.call($, navigator.userAgent)
1026 // make available to unit tests
1027 $.__detect = detect
1028
1029 })(Zepto)
1030
1031 /*
1032 事件处理部份
1033 */
1034 ;( function ($){
1035 var $$ = $.zepto.qsa, handlers = {}, _zid = 1, specialEvents= {},
1036 hover = { mouseenter: 'mouseover', mouseleave: 'mouseout' }
1037
1038 specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents'
1039
1040 // 取element的唯一标示符,如果没有,则设置一个并返回
1041 function zid(element) {
1042 return element._zid || (element._zid = _zid++ )
1043 }
1044 // 查找绑定在元素上的指定类型的事件处理函数集合
1045 function findHandlers(element, event, fn, selector) {
1046 event = parse(event)
1047 if (event.ns) var matcher = matcherFor(event.ns)
1048 return (handlers[zid(element)] || []).filter( function (handler) {
1049 return handler
1050 && (!event.e || handler.e == event.e) // 判断事件类型是否相同
1051 && (!event.ns || matcher.test(handler.ns)) // 判断事件命名空间是否相同
1052 // 注意函数是引用类型的数据zid(handler.fn)的作用是返回handler.fn的标示符,如果没有,则给它添加一个,
1053 // 这样如果fn和handler.fn引用的是同一个函数,那么fn上应该也可相同的标示符,
1054 // 这里就是通过这一点来判断两个变量是否引用的同一个函数
1055 && (!fn || zid(handler.fn) === zid(fn))
1056 && (!selector || handler.sel == selector)
1057 })
1058 }
1059 // 解析事件类型,返回一个包含事件名称和事件命名空间的对象
1060 function parse(event) {
1061 var parts = ('' + event).split('.' )
1062 return {e: parts[0], ns: parts.slice(1).sort().join(' ' )}
1063 }
1064 // 生成命名空间的正则
1065 function matcherFor(ns) {
1066 return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)' )
1067 }
1068 // 遍历events
1069 function eachEvent(events, fn, iterator){
1070 if ($.type(events) != "string" ) $.each(events, iterator)
1071 else events.split(/\s/).forEach( function (type){ iterator(type, fn) })
1072 }
1073 // 通过给focus和blur事件设置为捕获来达到事件冒泡的目的
1074 function eventCapture(handler, captureSetting) {
1075 return handler.del &&
1076 (handler.e == 'focus' || handler.e == 'blur') ||
1077 !! captureSetting
1078 }
1079
1080 // 修复不支持mouseenter和mouseleave的情况
1081 function realEvent(type) {
1082 return hover[type] || type
1083 }
1084
1085 // 给元素绑定监听事件,可同时绑定多个事件类型,如['click','mouseover','mouseout'],也可以是'click mouseover mouseout'
1086 function add(element, events, fn, selector, getDelegate, capture){
1087 var id = zid(element), set = (handlers[id] || (handlers[id] = [])) // 元素上已经绑定的所有事件处理函数
1088 eachEvent(events, fn, function (event, fn){
1089 var handler = parse(event)
1090 // 保存fn,下面为了处理mouseenter, mouseleave时,对fn进行了修改
1091 handler.fn = fn
1092 handler.sel = selector
1093 // 模仿 mouseenter, mouseleave
1094 if (handler.e in hover) fn = function (e){
1095 /*
1096 relatedTarget为事件相关对象,只有在mouseover和mouseout事件时才有值
1097 mouseover时表示的是鼠标移出的那个对象,mouseout时表示的是鼠标移入的那个对象
1098 当related不存在,表示事件不是mouseover或者mouseout,mouseover时!$.contains(this, related)当相关对象不在事件对象内
1099 且related !== this相关对象不是事件对象时,表示鼠标已经从事件对象外部移入到了对象本身,这个时间是要执行处理函数的
1100 当鼠标从事件对象上移入到子节点的时候related就等于this了,且!$.contains(this, related)也不成立,这个时间是不需要执行处理函数的
1101 */
1102 var related = e.relatedTarget
1103 if (!related || (related !== this && !$.contains( this , related)))
1104 return handler.fn.apply( this , arguments)
1105 }
1106 // 事件委托
1107 handler.del = getDelegate && getDelegate(fn, event)
1108 var callback = handler.del || fn
1109 handler.proxy = function (e) {
1110 var result = callback.apply(element, [e].concat(e.data))
1111 // 当事件处理函数返回false时,阻止默认操作和冒泡
1112 if (result === false ) e.preventDefault(), e.stopPropagation()
1113 return result
1114 }
1115 // 设置处理函数的在函数集中的位置
1116 handler.i = set.length
1117 // 将函数存入函数集中
1118 set.push(handler)
1119 element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
1120 })
1121 }
1122 // 删除绑定在元素上的指定类型的事件监听函数,可同时删除多种事件类型指定的函数,用数组或者还空格的字符串即可,同add
1123 function remove(element, events, fn, selector, capture){
1124 var id = zid(element)
1125 eachEvent(events || '', fn, function (event, fn){
1126 findHandlers(element, event, fn, selector).forEach( function (handler){
1127 delete handlers[id][handler.i]
1128 element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
1129 })
1130 })
1131 }
1132
1133 $.event = { add: add, remove: remove }
1134
1135 // 设置代理
1136 $.proxy = function (fn, context) {
1137 if ($.isFunction(fn)) {
1138 // 如果fn是函数,则申明一个新的函数并用context作为上下文调用fn
1139 var proxyFn = function (){ return fn.apply(context, arguments) }
1140 // 引用fn标示符
1141 proxyFn._zid = zid(fn)
1142 return proxyFn
1143 } else if ( typeof context == 'string' ) {
1144 return $.proxy(fn[context], fn)
1145 } else {
1146 throw new TypeError("expected function" )
1147 }
1148 }
1149
1150 $.fn.bind = function (event, callback){
1151 return this .each( function (){
1152 add( this , event, callback)
1153 })
1154 }
1155 $.fn.unbind = function (event, callback){
1156 return this .each( function (){
1157 remove( this , event, callback)
1158 })
1159 }
1160 // 绑定一次性事件监听函数
1161 $.fn.one = function (event, callback){
1162 return this .each( function (i, element){
1163 // 添加函数,然后在回调函数里再删除绑定。达到一次性事件的目的
1164 add( this , event, callback, null , function (fn, type){
1165 return function (){
1166 var result = fn.apply(element, arguments) // 这里执行绑定的回调
1167 remove(element, type, fn) // 删除上面的绑定
1168 return result
1169 }
1170 })
1171 })
1172 }
1173
1174 var returnTrue = function (){ return true },
1175 returnFalse = function (){ return false },
1176 ignoreProperties = /^([A-Z]|layer[XY]$)/ ,
1177 eventMethods = {
1178 preventDefault: 'isDefaultPrevented', // 是否调用过preventDefault方法
1179 // 取消执行其他的事件处理函数并取消事件冒泡.如果同一个事件绑定了多个事件处理函数, 在其中一个事件处理函数中调用此方法后将不会继续调用其他的事件处理函数.
1180 stopImmediatePropagation: 'isImmediatePropagationStopped', // 是否调用过stopImmediatePropagation方法,
1181 stopPropagation: 'isPropagationStopped' // 是否调用过stopPropagation方法
1182 }
1183 // 创建事件代理
1184 function createProxy(event) {
1185 var key, proxy = { originalEvent: event } // 保存原始event
1186 for (key in event)
1187 if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key] // 复制event属性至proxy
1188
1189 // 将preventDefault,stopImmediatePropagatio,stopPropagation方法定义到proxy上
1190 $.each(eventMethods, function (name, predicate) {
1191 proxy[name] = function (){
1192 this [predicate] = returnTrue
1193 return event[name].apply(event, arguments)
1194 }
1195 proxy[predicate] = returnFalse
1196 })
1197 return proxy
1198 }
1199
1200 // emulates the 'defaultPrevented' property for browsers that have none
1201 // event.defaultPrevented返回一个布尔值,表明当前事件的默认动作是否被取消,也就是是否执行了 event.preventDefault()方法.
1202 function fix(event) {
1203 if (!('defaultPrevented' in event)) {
1204 event.defaultPrevented = false // 初始值false
1205 var prevent = event.preventDefault // 引用默认preventDefault
1206 event.preventDefault = function () { // 重写preventDefault
1207 this .defaultPrevented = true
1208 prevent.call( this )
1209 }
1210 }
1211 }
1212 // 事件委托
1213 $.fn.delegate = function (selector, event, callback){
1214 return this .each( function (i, element){
1215 add(element, event, callback, selector, function (fn){
1216 return function (e){
1217 // 如果事件对象是element里的元素,取与selector相匹配的
1218 var evt, match = $(e.target).closest(selector, element).get(0 )
1219 if (match) {
1220 // evt成了一个拥有preventDefault,stopImmediatePropagatio,stopPropagation方法,currentTarge,liveFiredn属性的对象,另也有e的默认属性
1221 evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element})
1222 return fn.apply(match, [evt].concat([].slice.call(arguments, 1 )))
1223 }
1224 }
1225 })
1226 })
1227 }
1228 // 取消事件委托
1229 $.fn.undelegate = function (selector, event, callback){
1230 return this .each( function (){
1231 remove( this , event, callback, selector)
1232 })
1233 }
1234
1235 $.fn.live = function (event, callback){
1236 $(document.body).delegate( this .selector, event, callback)
1237 return this
1238 }
1239 $.fn.die = function (event, callback){
1240 $(document.body).undelegate( this .selector, event, callback)
1241 return this
1242 }
1243
1244 // on也有live和事件委托的效果,所以可以只用on来绑定事件
1245 $.fn.on = function (event, selector, callback){
1246 return !selector || $.isFunction(selector) ?
1247 this .bind(event, selector || callback) : this .delegate(selector, event, callback)
1248 }
1249 $.fn.off = function (event, selector, callback){
1250 return !selector || $.isFunction(selector) ?
1251 this .unbind(event, selector || callback) : this .undelegate(selector, event, callback)
1252 }
1253 // 主动触发事件
1254 $.fn.trigger = function (event, data){
1255 if ( typeof event == 'string' || $.isPlainObject(event)) event = $.Event(event)
1256 fix(event)
1257 event.data = data
1258 return this .each( function (){
1259 // items in the collection might not be DOM elements
1260 // (todo: possibly support events on plain old objects)
1261 if ('dispatchEvent' in this ) this .dispatchEvent(event)
1262 })
1263 }
1264
1265 // triggers event handlers on current element just as if an event occurred,
1266 // doesn't trigger an actual event, doesn't bubble
1267 // 触发元素上绑定的指定类型的事件,但是不冒泡
1268 $.fn.triggerHandler = function (event, data){
1269 var e, result
1270 this .each( function (i, element){
1271 e = createProxy( typeof event == 'string' ? $.Event(event) : event)
1272 e.data = data
1273 e.target = element
1274 // 遍历元素上绑定的指定类型的事件处理函数集,按顺序执行,如果执行过stopImmediatePropagation,
1275 // 那么e.isImmediatePropagationStopped()就会返回true,再外层函数返回false
1276 // 注意each里的回调函数指定返回false时,会跳出循环,这样就达到的停止执行回面函数的目的
1277 $.each(findHandlers(element, event.type || event), function (i, handler){
1278 result = handler.proxy(e)
1279 if (e.isImmediatePropagationStopped()) return false
1280 })
1281 })
1282 return result
1283 }
1284
1285 // shortcut methods for `.bind(event, fn)` for each event type
1286 ;('focusin focusout load resize scroll unload click dblclick '+
1287 'mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave '+
1288 'change select keydown keypress keyup error').split(' ').forEach( function (event) {
1289 $.fn[event] = function (callback) {
1290 return callback ?
1291 // 如果有callback回调,则认为它是绑定
1292 this .bind(event, callback) :
1293 // 如果没有callback回调,则让它主动触发
1294 this .trigger(event)
1295 }
1296 })
1297
1298 ;['focus', 'blur'].forEach( function (name) {
1299 $.fn[name] = function (callback) {
1300 if (callback) this .bind(name, callback)
1301 else this .each( function (){
1302 try { this [name]() }
1303 catch (e) {}
1304 })
1305 return this
1306 }
1307 })
1308
1309 // 根据参数创建一个event对象
1310 $.Event = function (type, props) {
1311 // 当type是个对象时
1312 if ( typeof type != 'string') props = type, type = props.type
1313 // 创建一个event对象,如果是click,mouseover,mouseout时,创建的是MouseEvent,bubbles为是否冒泡
1314 var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true
1315 // 确保bubbles的值为true或false,并将props参数的属性扩展到新创建的event对象上
1316 if (props) for ( var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name])
1317 // 初始化event对象,type为事件类型,如click,bubbles为是否冒泡,第三个参数表示是否可以用preventDefault方法来取消默认操作
1318 event.initEvent(type, bubbles, true , null , null , null , null , null , null , null , null , null , null , null , null )
1319 // 添加isDefaultPrevented方法,event.defaultPrevented返回一个布尔值,表明当前事件的默认动作是否被取消,也就是是否执行了 event.preventDefault()方法.
1320 event.isDefaultPrevented = function (){ return this .defaultPrevented }
1321 return event
1322 }
1323
1324 })(Zepto)
1325
1326 /* *
1327 Ajax处理部份
1328 * */
1329 ;( function ($){
1330 var jsonpID = 0 ,
1331 document = window.document,
1332 key,
1333 name,
1334 rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/ gi,
1335 scriptTypeRE = /^(?:text|application)\/javascript/ i,
1336 xmlTypeRE = /^(?:text|application)\/xml/ i,
1337 jsonType = 'application/json' ,
1338 htmlType = 'text/html' ,
1339 blankRE = /^\s*$/
1340
1341 // trigger a custom event and return false if it was cancelled
1342 function triggerAndReturn(context, eventName, data) {
1343 var event = $.Event(eventName)
1344 $(context).trigger(event, data)
1345 return ! event.defaultPrevented
1346 }
1347
1348 // trigger an Ajax "global" event
1349 // 触发 ajax的全局事件
1350 function triggerGlobal(settings, context, eventName, data) {
1351 if (settings.global) return triggerAndReturn(context || document, eventName, data)
1352 }
1353
1354 // Number of active Ajax requests
1355 $.active = 0
1356
1357 // settings.global为true时表示需要触发全局ajax事件
1358 // 注意这里的$.active++ === 0很巧妙,用它来判断开始,因为只有$.active等于0时$.active++ === 0才成立
1359 function ajaxStart(settings) {
1360 if (settings.global && $.active++ === 0) triggerGlobal(settings, null , 'ajaxStart' )
1361 }
1362 // 注意这里的 !(--$.active)同上面的异曲同工,--$.active为0,则表示$.active的值为1,这样用来判断结束,也很有意思
1363 function ajaxStop(settings) {
1364 if (settings.global && !(--$.active)) triggerGlobal(settings, null , 'ajaxStop' )
1365 }
1366
1367 // triggers an extra global event "ajaxBeforeSend" that's like "ajaxSend" but cancelable
1368 // 触发全局ajaxBeforeSend事件,如果返回false,则取消此次请求
1369 function ajaxBeforeSend(xhr, settings) {
1370 var context = settings.context
1371 if (settings.beforeSend.call(context, xhr, settings) === false ||
1372 triggerGlobal(settings, context, 'ajaxBeforeSend', [xhr, settings]) === false )
1373 return false
1374
1375 triggerGlobal(settings, context, 'ajaxSend' , [xhr, settings])
1376 }
1377 function ajaxSuccess(data, xhr, settings) {
1378 var context = settings.context, status = 'success'
1379 settings.success.call(context, data, status, xhr)
1380 triggerGlobal(settings, context, 'ajaxSuccess' , [xhr, settings, data])
1381 ajaxComplete(status, xhr, settings)
1382 }
1383 // type: "timeout", "error", "abort", "parsererror"
1384 function ajaxError(error, type, xhr, settings) {
1385 var context = settings.context
1386 settings.error.call(context, xhr, type, error)
1387 triggerGlobal(settings, context, 'ajaxError' , [xhr, settings, error])
1388 ajaxComplete(type, xhr, settings)
1389 }
1390 // status: "success", "notmodified", "error", "timeout", "abort", "parsererror"
1391 function ajaxComplete(status, xhr, settings) {
1392 var context = settings.context
1393 settings测试数据plete.call(context, xhr, status)
1394 triggerGlobal(settings, context, 'ajaxComplete' , [xhr, settings])
1395 ajaxStop(settings)
1396 }
1397
1398 // Empty function, used as default callback
1399 function empty() {}
1400 // 可参考http://zh.wikipedia.org/zh-cn/JSONP
1401 $.ajaxJSONP = function (options){
1402 if (!('type' in options)) return $.ajax(options)
1403
1404 var callbackName = 'jsonp' + (++jsonpID), // 创建回调函数名
1405 script = document.createElement('script' ),
1406 // js文件加载完毕
1407 cleanup = function () {
1408 clearTimeout(abortTimeout) // 清除下面的timeout事件处理
1409 $(script).remove() // 移除创建的script标签,因为该文件的JS内容已经解析过了
1410 delete window[callbackName] // 清除掉指定的回调函数
1411 },
1412 // 取消加载
1413 abort = function (type){
1414 cleanup()
1415 // In case of manual abort or timeout, keep an empty function as callback
1416 // so that the SCRIPT tag that eventually loads won't result in an error.
1417 // 这里通过将回调函数重新赋值为空函数来达到看似阻止加载JS的目的,实际上给script标签设置了src属性后,请求就已经产生了,并且不能中断
1418 if (!type || type == 'timeout') window[callbackName] = empty
1419 ajaxError( null , type || 'abort' , xhr, options)
1420 },
1421 xhr = { abort: abort }, abortTimeout
1422
1423 if (ajaxBeforeSend(xhr, options) === false ) {
1424 abort('abort' )
1425 return false
1426 }
1427 // 成功加载后的回调函数
1428 window[callbackName] = function (data){
1429 cleanup()
1430 ajaxSuccess(data, xhr, options)
1431 }
1432
1433 script.onerror = function () { abort('error' ) }
1434 // 将回调函数名追加到请求地址,并赋给script,至此请求产生
1435 script.src = options.url.replace(/=\?/, '=' + callbackName)
1436 $('head' ).append(script)
1437
1438 // 如果设置了超时处理
1439 if (options.timeout > 0) abortTimeout = setTimeout( function (){
1440 abort('timeout' )
1441 }, options.timeout)
1442
1443 return xhr
1444 }
1445
1446 // ajax全局设置
1447 $.ajaxSettings = {
1448 // Default type of request
1449 type: 'GET' ,
1450 // Callback that is executed before request
1451 beforeSend: empty,
1452 // Callback that is executed if the request succeeds
1453 success: empty,
1454 // Callback that is executed the the server drops error
1455 error: empty,
1456 // Callback that is executed on request complete (both: error and success)
1457 complete: empty,
1458 // The context for the callbacks
1459 context: null ,
1460 // Whether to trigger "global" Ajax events
1461 global: true ,
1462 // Transport
1463 xhr: function () {
1464 return new window.XMLHttpRequest()
1465 },
1466 // MIME types mapping
1467 accepts: {
1468 script: 'text/javascript, application/javascript' ,
1469 json: jsonType,
1470 xml: 'application/xml, text/xml' ,
1471 html: htmlType,
1472 text: 'text/plain'
1473 },
1474 // Whether the request is to another domain
1475 crossDomain: false ,
1476 // Default timeout
1477 timeout: 0 ,
1478 // Whether data should be serialized to string
1479 processData: true ,
1480 // Whether the browser should be allowed to cache GET responses
1481 cache: true ,
1482 }
1483
1484 // 根据MIME返回相应的数据类型,用作ajax参数里的dataType用,设置预期返回的数据类型
1485 // 如html,json,scirpt,xml,text
1486 function mimeToDataType(mime) {
1487 if (mime) mime = mime.split(';', 2)[0 ]
1488 return mime && ( mime == htmlType ? 'html' :
1489 mime == jsonType ? 'json' :
1490 scriptTypeRE.test(mime) ? 'script' :
1491 xmlTypeRE.test(mime) && 'xml' ) || 'text'
1492 }
1493 // 将查询字符串追加到URL后面
1494 function appendQuery(url, query) {
1495 // 注意这里的replace,将第一个匹配到的&或者&&,&?,? ?& ??替换成?,用来保证地址的正确性
1496 return (url + '&' + query).replace(/[&?]{1,2}/, '?' )
1497 }
1498
1499 // serialize payload and append it to the URL for GET requests
1500 // 序列化发送到服务器上的数据,如果是GET请求,则将序列化后的数据追加到请求地址后面
1501 function serializeData(options) {
1502 // options.processData表示对于非Get请求,是否自动将 options.data转换为字符串,前提是options.data不是字符串
1503 if (options.processData && options.data && $.type(options.data) != "string" )
1504 // options.traditional表示是否以$.param方法序列化
1505 options.data = $.param(options.data, options.traditional)
1506 if (options.data && (!options.type || options.type.toUpperCase() == 'GET' ))
1507 // 如果是GET请求,将序列化后的数据追加到请求地址后面
1508 options.url = appendQuery(options.url, options.data)
1509 }
1510
1511 $.ajax = function (options){
1512 // 注意这里不能直接将$.ajaxSettings替换掉$.extend的第一个参数,这样会改变 $.ajaxSettings里面的值
1513 // 这里的做法是创建一个新对象
1514 var settings = $.extend({}, options || {})
1515 // 如果它没有定义$.ajaxSettings里面的属性的时候,才去将$.ajaxSettings[key] 复制过来
1516 for (key in $.ajaxSettings) if (settings[key] === undefined) settings[key] = $.ajaxSettings[key]
1517 // 执行全局ajaxStart
1518 ajaxStart(settings)
1519
1520 // 通过判断请求地址和当前页面地址的host是否相同来设置是跨域
1521 if (!settings.crossDomain) settings.crossDomain = /^([\w-]+:)?\/\/([^\/]+)/.test(settings.url) &&
1522 RegExp.$2 != window.location.host
1523 // 如果没有设置请求地址,则取当前页面地址
1524 if (!settings.url) settings.url = window.location.toString();
1525 // 将data进行转换
1526 serializeData(settings);
1527 // 如果不设置缓存
1528 if (settings.cache === false ) settings.url = appendQuery(settings.url, '_=' + Date.now())
1529
1530 // 如果请求的是jsonp,则将地址栏里的=?替换为callback=?,相当于一个简写
1531 var dataType = settings.dataType, hasPlaceholder = /=\?/ .test(settings.url)
1532 if (dataType == 'jsonp' || hasPlaceholder) {
1533 if (!hasPlaceholder) settings.url = appendQuery(settings.url, 'callback=?' )
1534 return $.ajaxJSONP(settings)
1535 }
1536
1537 var mime = settings.accepts[dataType],
1538 baseHeaders = { },
1539 // 如果请求地址没有定请求协议,则与当前页面协议相同
1540 protocol = /^([\w-]+:)\/\//.test(settings.url) ? RegExp.$1 : window.location.protocol,
1541 xhr = settings.xhr(), abortTimeout
1542 // 如果没有跨域
1543 if (!settings.crossDomain) baseHeaders['X-Requested-With'] = 'XMLHttpRequest'
1544 if (mime) {
1545 baseHeaders['Accept'] = mime
1546 if (mime.indexOf(',') > -1) mime = mime.split(',', 2)[0 ]
1547 xhr.overrideMimeType && xhr.overrideMimeType(mime)
1548 }
1549 // 如果不是GET请求,设置发送信息至服务器时内容编码类型
1550 if (settings.contentType || (settings.contentType !== false && settings.data && settings.type.toUpperCase() != 'GET' ))
1551 baseHeaders['Content-Type'] = (settings.contentType || 'application/x-www-form-urlencoded' )
1552 settings.headers = $.extend(baseHeaders, settings.headers || {})
1553
1554 xhr.onreadystatechange = function (){
1555 if (xhr.readyState == 4 ) {
1556 xhr.onreadystatechange = empty;
1557 clearTimeout(abortTimeout)
1558 var result, error = false
1559 // 根据状态来判断请求是否成功
1560 // 状态>=200 && < 300 表示成功
1561 // 状态 == 304 表示文件未改动过,也可认为成功
1562 // 如果是取要本地文件那也可以认为是成功的,xhr.status == 0是在直接打开页面时发生请求时出现的状态,也就是不是用localhost的形式访问的页面的情况
1563 if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304 || (xhr.status == 0 && protocol == 'file:' )) {
1564 // 获取返回的数据类型
1565 dataType = dataType || mimeToDataType(xhr.getResponseHeader('content-type' ))
1566 result = xhr.responseText
1567
1568 try {
1569 // http://perfectionkills测试数据/global-eval-what-are-the-options/
1570 if (dataType == 'script') (1,eval)(result) // 如果返回的数据类型是JS
1571 else if (dataType == 'xml') result = xhr.responseXML
1572 else if (dataType == 'json') result = blankRE.test(result) ? null : $.parseJSON(result)
1573 } catch (e) { error = e }
1574 // 如果解析出错,则执行全局parsererror事件
1575 if (error) ajaxError(error, 'parsererror' , xhr, settings)
1576 // 否则执行ajaxSuccess
1577 else ajaxSuccess(result, xhr, settings)
1578 } else {
1579 // 如果请求出错,则根据xhr.status来执行相应的错误处理函数
1580 ajaxError( null , xhr.status ? 'error' : 'abort' , xhr, settings)
1581 }
1582 }
1583 }
1584
1585 var async = 'async' in settings ? settings.async : true
1586 xhr.open(settings.type, settings.url, async)
1587 // 设置请求头信息
1588 for (name in settings.headers) xhr.setRequestHeader(name, settings.headers[name])
1589
1590 // 如果ajaxBeforeSend函数返回的false,则取消此次请示
1591 if (ajaxBeforeSend(xhr, settings) === false ) {
1592 xhr.abort()
1593 return false
1594 }
1595
1596 // 当设置了settings.timeout,则在超时后取消请求,并执行timeout事件处理函数
1597 if (settings.timeout > 0) abortTimeout = setTimeout( function (){
1598 xhr.onreadystatechange = empty
1599 xhr.abort()
1600 ajaxError( null , 'timeout' , xhr, settings)
1601 }, settings.timeout)
1602
1603 // avoid sending empty string (#319)
1604 xhr.send(settings.data ? settings.data : null )
1605 return xhr
1606 }
1607
1608 // handle optional data/success arguments
1609 // 将参数转换成ajax函数指定的参数格式
1610 function parseArguments(url, data, success, dataType) {
1611 var hasData = !$.isFunction(data) // 如果data是function,则认为它是请求成功后的回调
1612 return {
1613 url: url,
1614 data: hasData ? data : undefined, // 如果data不是function实例
1615 success: !hasData ? data : $.isFunction(success) ? success : undefined,
1616 dataType: hasData ? dataType || success : success
1617 }
1618 }
1619
1620 // 简单的get请求
1621 $.get = function (url, data, success, dataType){
1622 return $.ajax(parseArguments.apply( null , arguments))
1623 }
1624
1625 $.post = function (url, data, success, dataType){
1626 var options = parseArguments.apply( null , arguments)
1627 options.type = 'POST'
1628 return $.ajax(options)
1629 }
1630
1631 $.getJSON = function (url, data, success){
1632 var options = parseArguments.apply( null , arguments)
1633 options.dataType = 'json'
1634 return $.ajax(options)
1635 }
1636
1637 // 这里的url可以是http://HdhCmsTestxxxx测试数据 selector这种形式,就是对加载进来的HTML对行一个筛选
1638 $.fn.load = function (url, data, success){
1639 if (! this .length) return this
1640 // 将请求地址用空格分开
1641 var self = this , parts = url.split(/\s/ ), selector,
1642 options = parseArguments(url, data, success),
1643 callback = options.success
1644 if (parts.length > 1) options.url = parts[0], selector = parts[1 ]
1645 // 要对成功后的回调函数进行一个改写,因为需要将加载进来的HTML添加进当前集合
1646 options.success = function (response){
1647 // selector就是对请求到的数据就行一个筛选的条件,比如只获取数据里的类名为.test的标签
1648 self.html(selector ?
1649 $('<div>').html(response.replace(rscript, "" )).find(selector)
1650 : response)
1651 // 这里才是你写的回调
1652 callback && callback.apply(self, arguments)
1653 }
1654 $.ajax(options)
1655 return this
1656 }
1657
1658 var escape = encodeURIComponent
1659
1660 function serialize(params, obj, traditional, scope){
1661 var type, array = $.isArray(obj)
1662 $.each(obj, function (key, value) {
1663 type = $.type(value)
1664 // scope用作处理value也是object或者array的情况
1665 // traditional表示是否以传统的方式拼接数据,
1666 // 传统的意思就是比如现有一个数据{a:[1,2,3]},转成查询字符串后结果为'a=1&a=2&a=3'
1667 // 非传统的的结果则是a[]=1&a[]=2&a[]=3
1668 if (scope) key = traditional ? scope : scope + '[' + (array ? '' : key) + ']'
1669 // handle data in serializeArray() format
1670 // 当处理的数据为[{},{},{}]这种情况的时候,一般指的是序列化表单后的结果
1671 if (!scope && array) params.add(value.name, value.value)
1672 // recurse into nested objects
1673 // 当value值是数组或者是对象且不是按传统的方式序列化的时候,需要再次遍历value
1674 else if (type == "array" || (!traditional && type == "object" ))
1675 serialize(params, value, traditional, key)
1676 else params.add(key, value)
1677 })
1678 }
1679 // 将obj转换为查询字符串的格式,traditional表示是否转换成传统的方式,至于传统的方式的意思看上面的注释
1680 $.param = function (obj, traditional){
1681 var params = []
1682 // 注意这里将add方法定到params,所以下面serialize时才不需要返回数据
1683 params.add = function (k, v){ this .push(escape(k) + '=' + escape(v)) }
1684 serialize(params, obj, traditional)
1685 return params.join('&').replace(/%20/g, '+' )
1686 }
1687 })(Zepto)
1688
1689 ;( function ($) {
1690 // 序列化表单,返回一个类似[{name:value},{name2:value2}]的数组
1691 $.fn.serializeArray = function () {
1692 var result = [], el
1693 // 将集合中的第一个表单里的所有表单元素转成数组后进行遍历
1694 $( Array.prototype.slice.call( this .get(0).elements) ).each( function () {
1695 el = $( this )
1696 var type = el.attr('type' )
1697 // 判断其type属性,排除fieldset,submi,reset,button以及没有被选中的radio和checkbox
1698 if ( this .nodeName.toLowerCase() != 'fieldset' &&
1699 ! this .disabled && type != 'submit' && type != 'reset' && type != 'button' &&
1700 // 注意这里的写法,当元素既不是radio也不是checkbox时,直接返回true,
1701 // 当元素是radio或者checkbox时,会执行后面的this.checked,当radio或者checkbox被选中时this.checked得到true值
1702 // 这样就可以筛选中被选中的radio和checkbox了
1703 ((type != 'radio' && type != 'checkbox') || this .checked))
1704 result.push({
1705 name: el.attr('name' ),
1706 value: el.val()
1707 })
1708 })
1709 return result
1710 }
1711 // 将表单的值转成name1=value1&name2=value2的形式
1712 $.fn.serialize = function () {
1713 var result = []
1714 this .serializeArray().forEach( function (elm) {
1715 result.push( encodeURIComponent(elm.name) + '=' + encodeURIComponent(elm.value) )
1716 })
1717 return result.join('&' )
1718 }
1719 // 表单提交
1720 $.fn.submit = function (callback) {
1721 if (callback) this .bind('submit' , callback)
1722 else if ( this .length) {
1723 var event = $.Event('submit' )
1724 this .eq(0 ).trigger(event)
1725 if (!event.defaultPrevented) this .get(0 ).submit()
1726 }
1727 return this
1728 }
1729
1730 })(Zepto)
1731
1732 // CSS3动画
1733 ;( function ($, undefined){
1734 var prefix = '' , eventPrefix, endEventName, endAnimationName,
1735 vendors = { Webkit: 'webkit', Moz: '', O: 'o', ms: 'MS' },
1736 document = window.document, testEl = document.createElement('div' ),
1737 supportedTransforms = /^((translate|rotate|scale)(X|Y|Z|3d)?|matrix(3d)?|perspective|skew(X|Y)?)$/ i,
1738 transform,
1739 transitionProperty, transitionDuration, transitionTiming,
1740 animationName, animationDuration, animationTiming,
1741 cssReset = {}
1742 // 将驼峰式的字符串转成用-分隔的小写形式,如borderWidth ==> border-width
1743 function dasherize(str) { return downcase(str.replace(/([a-z])([A-Z])/, '$1-$2' )) }
1744 function downcase(str) { return str.toLowerCase() }
1745 // 用于修正事件名
1746 function normalizeEvent(name) { return eventPrefix ? eventPrefix + name : downcase(name) }
1747
1748 // 根据浏览器的特性设置CSS属性前轻辍和事件前辍,比如浏览器内核是webkit
1749 // 那么用于设置CSS属性的前辍prefix就等于'-webkit-',用来修正事件名的前辍eventPrefix就是Webkit
1750 $.each(vendors, function (vendor, event){
1751 if (testEl.style[vendor + 'TransitionProperty'] !== undefined) {
1752 prefix = '-' + downcase(vendor) + '-'
1753 eventPrefix = event
1754 return false
1755 }
1756 })
1757
1758 transform = prefix + 'transform'
1759 cssReset[transitionProperty = prefix + 'transition-property'] =
1760 cssReset[transitionDuration = prefix + 'transition-duration'] =
1761 cssReset[transitionTiming = prefix + 'transition-timing-function'] =
1762 cssReset[animationName = prefix + 'animation-name'] =
1763 cssReset[animationDuration = prefix + 'animation-duration'] =
1764 cssReset[animationTiming = prefix + 'animation-timing-function'] = ''
1765
1766 $.fx = {
1767 off: (eventPrefix === undefined && testEl.style.transitionProperty === undefined),
1768 speeds: { _default: 400, fast: 200, slow: 600 },
1769 cssPrefix: prefix,
1770 transitionEnd: normalizeEvent('TransitionEnd' ),
1771 animationEnd: normalizeEvent('AnimationEnd' )
1772 }
1773
1774 $.fn.animate = function (properties, duration, ease, callback){
1775 if ($.isPlainObject(duration))
1776 ease = duration.easing, callback = duration测试数据plete, duration = duration.duration
1777 // 如果duration是数字时,表示动画持续时间,如果是字符串,则从$.fx.speeds中取出相对应的值,如果没有找到相应的值,对取默认值
1778 if (duration) duration = ( typeof duration == 'number' ? duration :
1779 ($.fx.speeds[duration] || $.fx.speeds._default)) / 1000
1780 return this .anim(properties, duration, ease, callback)
1781 }
1782
1783 $.fn.anim = function (properties, duration, ease, callback){
1784 var key, cssValues = {}, cssProperties, transforms = '' ,
1785 that = this , wrappedCallback, endEvent = $.fx.transitionEnd
1786 // 动画持续时间默认值
1787 if (duration === undefined) duration = 0.4
1788 // 如果浏览器不支持CSS3的动画,则duration=0,意思就是直接跳转最终值
1789 if ($.fx.off) duration = 0
1790
1791 // 如果properties是一个动画名keyframe
1792 if ( typeof properties == 'string' ) {
1793 // keyframe animation
1794 cssValues[animationName] = properties
1795 cssValues[animationDuration] = duration + 's'
1796 cssValues[animationTiming] = (ease || 'linear' )
1797 endEvent = $.fx.animationEnd
1798 } else {
1799 cssProperties = []
1800 // CSS transitions
1801 for (key in properties)
1802 // 如果设置 的CSS属性是变形之类的
1803 if (supportedTransforms.test(key)) transforms += key + '(' + properties[key] + ') '
1804 else cssValues[key] = properties[key], cssProperties.push(dasherize(key))
1805
1806 if (transforms) cssValues[transform] = transforms, cssProperties.push(transform)
1807 if (duration > 0 && typeof properties === 'object' ) {
1808 cssValues[transitionProperty] = cssProperties.join(', ' )
1809 cssValues[transitionDuration] = duration + 's'
1810 cssValues[transitionTiming] = (ease || 'linear' )
1811 }
1812 }
1813
1814 wrappedCallback = function (event){
1815 if ( typeof event !== 'undefined' ) {
1816 if (event.target !== event.currentTarget) return // makes sure the event didn't bubble from "below"
1817 $(event.target).unbind(endEvent, wrappedCallback)
1818 }
1819 $( this ).css(cssReset)
1820 callback && callback.call( this )
1821 }
1822 // 当可以执行动画的时候,那么动画结束后会执行回调,
1823 // 如果不支持持续动画,在直接设置最终值后,不会执行动画结束回调
1824 if (duration > 0) this .bind(endEvent, wrappedCallback)
1825
1826 // trigger page reflow so new elements can animate
1827 this .size() && this .get(0 ).clientLeft
1828
1829 // 设置
1830 this .css(cssValues)
1831
1832 // 当持续时间小于等于0时,立刻还原
1833 if (duration <= 0) setTimeout( function () {
1834 that.each( function (){ wrappedCallback.call( this ) })
1835 }, 0 )
1836
1837 return this
1838 }
1839
1840 testEl = null
1841 })(Zepto)
分类: javascript
标签: js zepto
作者: Leo_wl
出处: http://HdhCmsTestcnblogs测试数据/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息声明:本文来自网络,不代表【好得很程序员自学网】立场,转载请注明出处:http://haodehen.cn/did46469