/*!
* Includes Sizzle.js 选择器,独立的库
* http://sizzlejs.com/
*/
(function( window, undefined ) {
//"use strict";
var
// rootjQuery = jQuery(document) = $();压缩有用
rootjQuery, // dom是否加载完
readyList, // core_strundefined == 'undefined'
core_strundefined = typeof undefined, // Use the correct document accordingly with window argument (sandbox)
location = window.location,
document = window.document,
docElem = document.documentElement, // 接受外部对jQuery,$的改变,报讯在_jQuery,_$中,
_jQuery = window.jQuery,
_$ = window.$, //class2type = { '[Object String]' : 'string' , '[Object Array]' : 'array' }
class2type = {}, // 没有用处
core_deletedIds = [], core_version = "2.0.3", // 将对象方法的地址保存在变量中,后面用call调用。
/*
var core_deletedIds = [],
core_version = "2.0.3",
core_push = core_deletedIds.push,
core_trim = core_version.trim;
alert(core_push);
function F(){
this.pu = core_push;
this.tr = core_trim;
this.ar = new Array();
this.df = function(){}
}
var f1 = new F();
core_push.call(f1.ar,1,2,3,4);
f1.pu.call(f1.ar,1,2,3,4)
//f.ar.core_push(1,2,3,4); //f.ar没有core_push方法
alert(f1.ar.toString()); var f2 = new F();
alert(f2.pu === f1.pu);//TRUE
alert(f2.df === f1.df);//false
alert(f2.tr === f1.tr);//TRUE
alert(f2.ar === f1.ar);//false function F(){
this.a = 1;
this.f = function(){alert(2);}
}
var f1 = new F();
var f2 = new F();
alert(f1.f == f2.f);//false
var f1f = f1.f; function N(){
this.f = f1f;
}
var n1 = new N();
var n2 = new N();
alert(n1.f == n2.f);//true
alert(n1.f === n2.f);//true
*/
core_concat = core_deletedIds.concat,
core_push = core_deletedIds.push,
core_slice = core_deletedIds.slice,
core_indexOf = core_deletedIds.indexOf,
core_toString = class2type.toString,
core_hasOwn = class2type.hasOwnProperty,
core_trim = core_version.trim, // []表示任意一个,?表示0、1个,\d表示数字,*表示任意多个,.单个非回车换行的字符,\.字符点,|是或者,\d*\.| 数字加小数点或者什么都没有,[eE]科学计数法
core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source, // \S是空格,按照空格分割成数组
core_rnotwhite = /\S+/g, // 正则中不加g的时候,会把整体匹配到还会匹配整项,?:表示外层的括号不是子项,小括号是2个子项,
//match = ['#div1',null,'div1']; //$('#div1')
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]* | #([\w-]*))$/, // 匹配空标签<p></p>
rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, // Matches dashed string for camelizing
rmsPrefix = /^-ms-/,
rdashAlpha = /-([\da-z])/gi, // Used by jQuery.camelCase as callback to replace()
fcamelCase = function( all, letter ) {
return letter.toUpperCase();
}, //匿名函数全局作用域,dom加载完成执行的方法
completed = function() {
document.removeEventListener( "DOMContentLoaded", completed, false );
window.removeEventListener( "load", completed, false );
jQuery.ready();
}; /*
function Aaa(){
}
Aaa.prototype.init = function(){
};
Aaa.prototype.css = function(){
};
var a1 = new Aaa();
a1.init();//prototype里的方法一般用对象调用
a1.css(); function jQuery(){
return new jQuery.prototype.init();//返回init类的对象
}
jQuery.prototype.init = function(){
};
jQuery.prototype.css = function(){
};
jQuery.prototype.init.prototype = jQuery.prototype;//init类的原型等于jQuery的原型,init对象就可以使用jQuery原型里的方法
//外部使用
jQuery().css(); */ // $() == jQuery(),这个函数返回对象,
jQuery = function( selector, context ) {
//jQuery.fn=jQuery.prototype={init:function(){}},fn就指向prototype,init就是prototype里面的方法,一般用类的对象来调用。
//jQuery()、$()返回的是init()类的对象,并且执行了init函数里面的方法,执行初始化。
//$(),jQuery()传的参数都是传给了init()方法里面去了
return new jQuery.fn.init( selector, context, rootjQuery );
},
jQuery.fn.init.prototype = jQuery.fn;//init类的原型等于jQuery的原型,init对象就可以使用jQuery原型里的方法 jQuery.fn = jQuery.prototype = { //给类jQuery增加静态属性fn,修改函数的prototype并增加属性和方法
jquery: core_version, constructor: jQuery,//不回指就是object
//$("a,b,c")都是调用的是init方法,new init(a,b,c),
//$('li','ul')//选择ul下面的li,
//return new jQuery.fn.init(),init函数执行,如果有return就返回return里面的东西(return的要是引用对象,否则返回init类的实例对象)。
init: function( selector, context, rootjQuery ) {
var match, elem; // $(""), $(null), $(undefined), $(false) 判空
if ( !selector ) {
return this;//返回init函数类的对象
} // $('#div1') $('.box') $('div') $('#div1 div.box') $('<li>') $('<li>1</li><li>2</li>')//创建字符串标签,$('li','ul')//选择ul下面的li, 都是strings类型。
if ( typeof selector === "string" ) {
if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
// 标签<p>,match是进来的时候定义的,拼接数组,
//$('<li>') $('<li>1</li><li>2</li>')
//match = [ null, '<li>', null ];
//match = [ null, '<li>1</li><li>2</li>', null ];
match = [ null, selector, null ]; } else {
/*不是标签,$('#div1') $('.box') $('div') $('#div1 div.box') $('<li>hello')
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]* | #([\w-]*))$/,
match = null; //$('.box') $('div') $('#div1 div.box')
match = ['#div1',null,'div1']; //$('#div1')
match = ['<li>hello','<li>',null]; //$('<li>hello')
*/
match = rquickExpr.exec( selector );
} // 创建标签或者根据id查找,$('<li>') $('#div1')
if ( match && (match[1] || !context) ) { //创建标签 $('<li>')
if ( match[1] ) {
//第二个参数context是创建标签的上下文,$('<li>',$(document)),用于iframe中,
context = context instanceof jQuery ? context[0] : context; // $('<li>1</li><li>2</li>').appendTo( ' ul ' );
//merge方法 把 $('<li>1</li><li>2</li>')变成json = {0 : 'li',1 : 'li',length : 2},return this就是这个json并返回出去,就可以appendTo到ul上了。$()返回你需要的任意类型this对象。
jQuery.merge( this, jQuery.parseHTML(
//parseHTML方法把<li>1</li><li>2</li>变成li数组[li,li],match = [ null, '<li>1</li><li>2</li>', null ];
//new的时候this指的是本类对象,
match[1],
context && context.nodeType ? context.ownerDocument || context : document,
true
) ); // 添加标签并且加属性,$('<li></li>',{title : 'hi',html : 'abcd',css : {background:'red'}}).appendTo( 'ul' );
//rsingleTag正则匹配单标签。<li>、<li></li>可以,<li></li><li></li>不行,isPlainObject判断第二个参数是json
if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
for ( match in context ) {
// {html:abcd},this[html]是一个函数,就执行这个函数this[html]('abcd'),
if ( jQuery.isFunction( this[ match ] ) ) {
this[ match ]( context[ match ] ); // {title:'hi'},this.attr(title,'hi'),
} else {
this.attr( match, context[ match ] );
}
}
} return this; // $('#div1') ,id选择器,match = ['#div1',null,'div1'];
} else {
elem = document.getElementById( match[2] );//根据id获取element, //Blackberry 4.6仅仅判断elem存在是不行的,还要判断他父节点存在
if ( elem && elem.parentNode ) {
// Inject the element directly into the jQuery object
this.length = 1;
this[0] = elem;//给对象添加属性名为0值为elem,this[0]设置获取值,不能通过this.0设置获取值。
/*
function F(a){
return new init();//this是F对象
}
function init(a){
this.length = 1;
this[0] = document.getElementById("a");
alert(this[0].innerHTML);
return this;
}
var f = F();
alert(f[0].innerHTML);
*/
} this.context = document;
this.selector = selector;//'#div1'
return this;
} // HANDLE: $(expr, $(...)) $('.box') $('div') $('#div1 div.box')
//jquery: core_version = "2.0.3",
} else if ( !context || context.jquery ) {
//context不存在或者context存在且context的jquery存在,是jquery对象
//( context || rootjQuery )context存在不走后面,rootjQuery = jQuery(document),context.find() 或者 rootjQuery.find
//$('ul',$(document)).find('li');走这里,$(document)是init对象有jquery属性,jquery是init方法的属性,就是判断context是不是init对象或者说jQuery对象。
return ( context || rootjQuery ).find( selector ); // HANDLE: $(expr, context)
// (which is just equivalent to: $(context).find(expr)
} else {
//context存在并且context的jquery属性不存在,不是jQuery对象,构造一个jquery对象
//$('ul',document).find('li');走这里
//constructor: jQuery,constructor就是jQuery函数,jQuery(document).find,window.$ = jQuery;相当于$(document).find,
//constructor和函数名一样,都是指的是函数地址,
return this.constructor( context ).find( selector );
} // selector !== "string" : $(DOMElement),$(this) $(document)
} else if ( selector.nodeType ) {//节点都有nodeType属性
this.context = this[0] = selector;
this.length = 1;
return this; // HANDLE: $(function) $(function(){})
//$(function(){}) 是后面的简写 $(document).ready(function(){});
} else if ( jQuery.isFunction( selector ) ) {
// rootjQuery = jQuery(document);
return rootjQuery.ready( selector );
}
//$([]) $({}) $( $('#div1') ) === $('#div1') ,如果传的是jquery对象$('#div1') ,selector = $('#div1'),$('#div1')这个对象有selector属性,selector.selector === '#div1',
if ( selector.selector !== undefined ) {
this.selector = selector.selector;//'#div1'
this.context = selector.context;//document
} return jQuery.makeArray( selector, this );//把类似于数组的转成真正的数组,
}, // 选择到的元素的字符串
selector: "", // $('') 返回的new init(),是init对象,或者init方法中返回的引用对象,对象中元素的个数,
length: 0, //jQuery函数原型的方法,jQuery函数对象的方法,makeArray是jQuery extend的方法
// $('div')) : json对象 Object { 0: <div>, 1: <div>, 2: <div>, 3: <div>, length: 4, prevObject: Object, context: HTMLDocument → new%202.html, selector: "div" }
//$('div').toArray() : Array [ <div>, <div>, <div>, <div> ]
toArray: function() {//json转数组
return core_slice.call( this );
}, // $('div').get(-1).innerHTML = '222222222',$('div')是jQuery返回的this对象,$('div').get()返回的是div的数组,this[ num ]中括号除了数组的下标还可以代表json或者对象的属性。
get: function( num ) {
return num == null ?this.toArray() :
( num < 0 ? this[ this.length + num ] : this[ num ] );
}, // Take an array of elements and push it onto the stack (returning the new matched element set)
//$('div').pushStack( $('span') ) $('span').prevObject -> $('div')
pushStack: function( elems ) {
/*
jQuery = function() {
alert(1);
return new jQuery.fn.init();
},
jQuery.fn = jQuery.prototype = {//fn是类的静态属性
//能够调用jQuery原型里(constructor()、pushStack())方法的只能是jQuery对象或者init对象:$('div')。
constructor: jQuery,
init: function() {
},
//$('div').pushStack,this是$('div'),
pushStack: function() {
alert(2);
this.constructor();//1, this是init对象或者jQuery对象$('div'),返回的是新的空init对象
constructor();//constructor.call(window),调不了报错,
},
}
jQuery.fn.init.prototype = jQuery.fn;
var j = constructor();//window调用不了
$('div').pushStack();//1,2,1, $('div')是init对象可以调用pushStack()方法
o = {}
o.pushStack();//o.pushStack is not a function,能够调用pushStack方法是jQuery的对象后者init对象,
new jQuery.fn.init().pushStack();//2 1
*/
//this是调用pushStack方法的对象,能够调用pushStack和constructor()方法的只有jQuery的对象后者init对象,这里this是$('div')(json格式),this.constructor()返回的是新的空参数的init对象(有jQuery原型的属性和方法以及init函数类的属性方法)
var ret = jQuery.merge( this.constructor(), elems );//返回span集合,elems = $('span')
ret.prevObject = this;//this = $('div'),$('span').prevObject -> $('div')
ret.context = this.context;
return ret;//将$('span')返回出去
/*
$('div').pushStack( $('span') ),pushStack()返回span,span的prevObject等于div,css('background','red')只会把span变红,
end: function() {
return this.prevObject || this.constructor(null);
},
调用end方法就是返回prevObject就实现了栈,多次调用end(),最后返回空,就不处理最后的css方法。 $('div').pushStack( $('span') ).css('background','red').end().end().end().css('background','yellow');
*/
}, //加强版的for循环,each是原型的方法,就是对象的方法,
each: function( callback, args ) {
return jQuery.each( this, callback, args );//这个each是jQuery.extend({})加进去的, }, ready: function( fn ) {
jQuery.ready.promise().done( fn );
return this;
},
/*
core_slice = core_deletedIds.slice, core_deletedIds = [],
$('div').slice(1,3)返回2.3两个div,他的prevObject = $('div')(4个div),css('background','red')就是把2个div变红,end().css('color','blue')把4个div变蓝。 $('div').slice(1,3).css('background','red').end().css('color','blue');
*/
slice: function() {
return this.pushStack( core_slice.apply( this, arguments ) );
}, first: function() {
return this.eq( 0 );
}, last: function() {
return this.eq( -1 );
},
//$('div').eq(0).css('background','red');
eq: function( i ) {
var len = this.length,//4个div
j = +i + ( i < 0 ? len : 0 );
return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );//返回div[0],div[0].prevObject = $('div')(4个div),
},
/*var arr = ['a','b','c'];
arr = $.map(arr,function(elem,i){
return elem + i;
});
alert( arr );//[a0,b1,c2]*/
map: function( callback ) {
return this.pushStack( jQuery.map(
this,
function( elem, i ) {
return callback.call( elem, i, elem );
}
)
);
}, end: function() {
return this.prevObject || this.constructor(null);
}, //内部使用,把数组的方法挂载到了jQuery对象上了,
push: core_push,//core_push = core_deletedIds.push,core_deletedIds = [],
sort: [].sort,
splice: [].splice
}; //给jQuery类添加静态方法extend,给jQuery的原型添加方法就是添加对象的方法
/*
$.extend({ //给jQuery类加静态方法
aaa : function(){
alert(1);
},
bbb : function(){
alert(2);
}
}); $.fn.extend({ //给jQuery对象加方法
aaa : function(){
alert(3);
},
bbb : function(){
alert(4);
}
});
$.aaa();//1
$.bbb();//2
$().aaa();//3
$().bbb();//4
$.extend(); -> this -> $(jQuery类) -> this.aaa -> $.aaa()
$.fn.extend(); -> this -> $.fn(jQuery原型对象) -> this.aaa -> $().aaa()
//当写多个对象自变量的时候 , 后面的对象都是扩展到第一个对象身上
var a = {};
$.extend( a , { name : 'hello' } , { age : 30 } );
console.log( a );// { name : 'hello' , age : 30 } 深拷贝浅拷贝
*/
jQuery.extend = jQuery.fn.extend =
{function() {
//extend继承方法的定义,extend就是给自变量的对象或者jQuery类jQuery对象扩展另一个对象的属性。就是涉及到深拷贝浅拷贝。
var options, name, src, copy, copyIsArray, clone,
//默认目标元素是第一个参数,arguments[0],i = 1,不做深拷贝deep = false;
//var a = {};$.extend( a , { name : 'hello',age:12 } , { ss : 30 }
target = arguments[0] || {},//第一个参数
i = 1,
length = arguments.length,
deep = false;//默认是浅拷贝 /* 深拷贝
var a = {};
var b = { name : { age : {nn:14} } };
$.extend( true,a , b );
*/
if ( typeof target === "boolean" ) {//判断第一个参数是不是布尔值
deep = target;//true 深拷贝
target = arguments[1] || {};//目标元素是第二个参数
i = 2;//目标元素是第二个参数
} // 第一个元素不是对象也不是函数,是string或其他,有可能是深拷贝,设置目标元素是空对象
if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
target = {};
} // 只有一个参数,$.extend({aaa : function(){alert(1);},bbb : function(){alert(2);}),$.fn.extend({aaa : function(){alert(1);},bbb : function(){alert(2);})
});
if ( length === i ) {
target = this;//目标参数就是this,this是$或者$.fn,this指的是函数时候代表函数体,
--i;//i=0
} //多个参数,循环把每个对象赋值到第一个对象,
//1.var a = {};$.extend( a , { name : 'hello',age:12 } , { ss : 30 } );target=a,deep = false,i = 1,
//2.$.extend({aaa : function(){alert(1);},bbb : function(){alert(2);}),i=0,deep = false
//3.1.var a = {}; var b = { name : {age : {nn:14} } }; $.extend( true,a , b );deep = true,target=a={},i = 2
for ( ; i < length; i++ ) {
//1.判空,不能是$.extend( a , null , null ); 1.options是第i个json,
//2.options={aaa : function(){alert(1);},bbb : function(){alert(2);}
//3.1.options= { name : { age : {nn:14} } }
if ( (options = arguments[ i ]) != null ) {
//1.遍历第i个json,{ name : 'hello',age:12 }
for ( name in options ) {
//1.target = a = {},src = a[name] = null,a[age] = null,a[ss]=null
//2.$[aaa]=null=src,target=$,$[bbb]=null=src
//3.1.a[name]=src=null,
src = target[ name ];
//1.copy = 'hello' copy=12 copy=30
//2.copy=function(){alert(1);},copy=function(){alert(2);},
//3.1.copy={age : {nn:14} }
copy = options[ name ]; // 1. $.extend( a , { name : a } )防止这种情况
//2.$.extend({aaa : $,bbb : $)防止这种情况
//3.1.$.extend( true,a , {name:a} )
if ( target === copy ) {
continue;
} // 深拷贝
//3.1.isPlainObject判断json键对应的值是不是对象,或者数组
//copy这个键的值是对象
if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
//src = target[ name ] 如果存在,target[ name ] = jQuery.extend( deep, target[ name ], copy );
if ( copyIsArray ) {
copyIsArray = false;
clone = src && jQuery.isArray(src) ? src : []; } else {
/*3.1.3 clone=false
var a = { name : { job : 'it' } };
var b = { name : {age : {n:10}} };
$.extend( true , a , b );
src = { job : 'it' }
a = { name : { job : 'it' ,{age : {n:10}}} };
*/
clone = src && jQuery.isPlainObject(src) ? src : {};
} // 3.递归
//3.1.a[name]=jQuery.extend( true, {}, {age : {nn:14} } )
//3.1.2 a[name]=jQuery.extend( true, { job : 'it' }, {age : {n:10}})
//3.1.2 a[age]=jQuery.extend( true, {}, {n:10} )={n:10},a={job : 'it',age : {n:10}}
//3.1.3 a[name] = jQuery.extend( true, { job : 'it' }, {age : {n:10}} ); target={job : 'it'} target[age]=jQuery.extend( deep, {}, {n:10} );target={} target[n]=10 return{n:10},target[age]={n:10},target={job : 'it',age : {n:10}} return target,a[name]={job : 'it',age : {n:10}}
target[ name ] = jQuery.extend( deep, clone, copy ); //浅拷贝target[ name ] = copy;copy是一个对象的时候就是传递的地址,
} else if ( copy !== undefined ) {
//1.a[name] = 'hello' a[age] = 12 a[ss]=30 a={name:'hello',age:12,ss:30}
//2.$[aaa]=function(){alert(1);},$[bbb]=function(){alert(2);}
target[ name ] = copy;
}
}
}
}
return target;
}
};
jQuery.extend({//调用extend方法,给jQuery函数增加静态方法,js中直接通过$调用,不需要jquery对象。 // 唯一随机字符串,replace( /\D/g, "" )把非数字小数点变为空,例如数据缓存做唯一操作
expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ),
/*
<script>
var $ = 123;
var jQuery = 456;
js源码里面有 _jQuery = window.jQuery,_$ = window.$,接受外部的改变,保存在_$ = 123,_jQuery=456
</script>
<script src="jquery-203.js"></script>
<script>
var miaov = $.noConflict(true);
</script>
*/
noConflict: function( deep ) {
if ( window.$ === jQuery ) {//一定相等
window.$ = _$;//window.$ = 123,jQuery就放弃$了,使用miaov,
} if ( deep && window.jQuery === jQuery ) {//一定相等
window.jQuery = _jQuery;//window.jQuery = 456,jQuery就放弃jQuery了,使用miaov,
} return jQuery;
}, // Is the DOM ready to be used? Set to true once it occurs.
isReady: false, // A counter to track how many items to wait for before
// the ready event fires. See #6781
readyWait: 1, /*
$.getScript('a.js',function(){//动态加载js,异步加载,有可能alert(2)先于a.js加载完
});
$(function(){
alert(2);
});
-----------------------------------------
$.holdReady(true);
$.getScript('a.js',function(){//动态加载js,异步加载
$.holdReady(false);//a.js加载完后回调函数执行,释放hold,保证先加载a.js,然后弹出alert(2)
});
$(function(){
alert(2);
});
*/
holdReady: function( hold ) {
if ( hold ) {
jQuery.readyWait++;//readyWait是将要加到jQery函数的属性,加完之后调用这个方法就可以使用这个变量,holdReady调用多次,readyWait++多次
} else {
jQuery.ready( true );//下面
}
}, //$.ready() Handle when the DOM is ready
ready: function( wait ) { // --jQuery.readyWait不为0就继续等
if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
return;
} // isReady是给jQuery类扩展的静态属性
jQuery.isReady = true; // If a normal DOM Ready event fired, decrement, and wait if need be
if ( wait !== true && --jQuery.readyWait > 0 ) {
return;
} // If there are functions bound, to execute
readyList.resolveWith( document, [ jQuery ] ); // Trigger any bound ready events
if ( jQuery.fn.trigger ) {
jQuery( document ).trigger("ready").off("ready");
}
}, // See test/unit/core.js for details concerning isFunction.
// Since version 1.3, DOM methods and functions like alert
// aren't supported. They return false on IE (#2968).
isFunction: function( obj ) {
return jQuery.type(obj) === "function";
}, isArray: Array.isArray, isWindow: function( obj ) {// $.isWindow(window),通过$调用。js中window有2个作用,1是充当全部对象,2是做浏览器窗口,只有window才有window属性并且等于window。如果obj是undefined、null,undefined === undefined.window=undefined,null === null.window=undefined,所以要判断obj不是undefined或者null(obj != null)。
return obj != null && obj === obj.window;//undefined是等于null的,null是等于null的,undefined.window返回null,null.window返回null,
}, //不能用typeof,alert( typeof NaN );//number
isNumeric: function( obj ) {
//parseFloat(obj) 可以转的是数字,不可以转的返回NaN,
//isFinite是原生的方法,判断是不是有限的数字,alert( isFinite( Number.MAX_VALUE + Number.MAX_VALUE ) );//false
return !isNaN( parseFloat(obj) ) && isFinite( obj );
}, type: function( obj ) {
if ( obj == null ) {//obj = null或者undefined
return String( obj );//'undefined','null'
}
//core_toString = {}.toString,
//alert( {}.toString.call([]));//[object Array]这个判断类型最全
//jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { class2type[ "[object " + name + "]" ] = name.toLowerCase(); });将[object Array]简写成array
return typeof obj === "object" || typeof obj === "function" ?
class2type[ core_toString.call(obj) ] || "object" :
typeof obj;
}, //判断是否对象自变量
isPlainObject: function( obj ) {
//jQuery.type( obj ) !== "object" 不是基本类型
//obj.nodeType 是一个dom节点,jQuery.type(dom节点)返回object,不是对象自变量,window也不是对象自变量,jQuery.type(window)返回object
if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
return false;
}
//jQuery.type( window.location) 返回object,不是基本类型不是节点不是window,
//有constructor属性,core_hasOwn = {}.hasOwnProperty方法,obj.constructor.prototype有没有isPrototypeOf属性,
try {
if ( obj.constructor &&
!core_hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) {
return false;
}
} catch ( e ) {
return false;//狐火下执行次数过多会报错
} //{},new Object(),
return true;
}, //是否为空对象{},[],没有自身的属性和方法(有原型属性也可以遍历出来,不是空对象)
isEmptyObject: function( obj ) {
var name;
for ( name in obj ) {
return false;
}
return true;
}, error: function( msg ) {// $.error('这是错误');
throw new Error( msg );
}, /* keepScripts = true, 可以解析script标签
把 $('<li>1</li><li>2</li>')变成this = {0 : 'li',1 : 'li',length : 2},data = [ null, '<li>1</li><li>2</li>', null ];
/*
var str = '<li></li><li></li>';
$.parseHTML(str); 返回li的数组
/*[li, li]
0: li
1: li
length: 2
__proto__: Array(0) // var str = '<li></li><li></li><script><\/script>';
$.parseHTML(str,document,true);
//
[li, li, script]
0: li
1: li
2: script
length: 3
__proto__: Array(0)
//
*/
parseHTML: function( data, context, keepScripts ) {
if ( !data || typeof data !== "string" ) {//存不存在,是不是字符串
return null;
}
if ( typeof context === "boolean" ) {//第二个参数就是布尔值,就当第三个参数用,第二个参数是false,
keepScripts = context;
context = false;
}
context = context || document;//执行上下文只能是document,这个document可以是当前页面的也可以是iframe的,
var parsed = rsingleTag.exec( data ),//判断是不是单标签<li></li>,<li></li><li></li>是多标签
scripts = !keepScripts && []; // 单标签,创建这个标签,返回数组,
if ( parsed ) {
return [ context.createElement( parsed[1] ) ];//context是执行上下文,是document对象,创建element和getelement前面都需要这个对象来调用方法而已。
}
//多标签,创建dom标签节点
parsed = jQuery.buildFragment( [ data ], context, scripts ); if ( scripts ) {//keepScripts=true,不删除script标签,keepScripts=false,删除script标签。
jQuery( scripts ).remove();
} return jQuery.merge( [], parsed.childNodes );
}, //json格式字符串转json,IE678不支持,
parseJSON: JSON.parse, // Cross-browser xml parsing,解析xml,出了json解析还有xml解析,xml是最早的数据形式,后来才有的json,以前都是xml。
parseXML: function( data ) {
var xml, tmp;
if ( !data || typeof data !== "string" ) {//不存在不是string
return null;
} // Support: IE678不支持,支持IE9。IE678出错了不会进入catch,而是创建一个xml携带<parsererror>错误的信息</parsererror>
try {
tmp = new DOMParser();//创建解析xml的对象,最早火狐支持,后来都开始支持了。
xml = tmp.parseFromString( data , "text/xml" );
} catch ( e ) {
xml = undefined;//data必须是完整的xml,不能是html,
} if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) {
jQuery.error( "Invalid XML: " + data );
}
return xml;
}, noop: function() {}, // Evaluates a script in a global context,全局解析js,
/*
function test() {
jQuery.globalEval( "var newVar = true;" )
var newVar1 = true;
}
test();
alert( newVar );
alert( newVar1 ); function test(){
var i = eval;
i('var a = 1');
eval('var b = 2');
window.eval('var c = 3');
alert( "s"+a );
alert( "s"+b );
alert( "s"+c ); }
test();
alert( a );//1
alert( b );//buxing
alert( c );//3
*/
globalEval: function( code ) {
var script,
indirect = eval; code = jQuery.trim( code );//去前后空格 if ( code ) {
// 创建script标签,加文本,加入到head中,删除script标签
if ( code.indexOf("use strict") === 1 ) {
script = document.createElement("script");
script.text = code;
document.head.appendChild( script ).parentNode.removeChild( script );
} else {
indirect( code );//eval来解析
}
}
}, // 把css样式转成js能够接受的,比如js不能接受横杆
/*
(function(){
var a,b=1,c=2;//全局闭包作用域,内部都可以通过闭包链来找到这个外部变量
var f = function(o){
for(i in o){
alert(i+"--"+o[i]());//camelCase--3
}
}
f({
camelCase: function( string ) {
return b+c;
},
})
})()
*/
//margin-top -> marginTop -ms-transform -> msTransform -moz-transform -> MozTransform
//rmsPrefix = /^-ms-/, rdashAlpha = /-([\da-z])/gi, fcamelCase = function( all, letter ) { return letter.toUpperCase(); },
camelCase: function( string ) {
return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
},
/*
replace后面传递函数:
rmsPrefix = /^-ms-/;
rdashAlpha = /-([\da-z])/gi;
fcamelCase = function( all, letter ) {
alert(all);//-t,正则匹配到的整体
alert(letter);//t,正则里面的子项
return letter.toUpperCase();
};
camelCase=function( string ) {
var d = string.replace( rmsPrefix, "ms-" );//ms-transform
var g = d.replace( rdashAlpha, fcamelCase );//msTransform
return g;
},
alert(camelCase('-ms-transform'));
*/ //判断节点的名字是不是指定的名字
nodeName: function( elem, name ) {
return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
}, // args is for 内部使用
/**
var arr = ['a','b','c','d'];
var json = { name : 'hello' , age : 20 };
$.each(json , function(i,value){//下标 每一个值
alert(value);
return false;//return false了,只执行一次
});
*/
each: function( obj, callback, args ) {
var value,
i = 0,
length = obj.length,//数组有长度,json没有长度,也有可能json里面有一个length属性,
isArray = isArraylike( obj );//是不是类数组或者真正数组 if ( args ) {//内部使用,平时不传
if ( isArray ) {
for ( ; i < length; i++ ) {
value = callback.apply( obj[ i ], args ); if ( value === false ) {
break;
}
}
} else {
for ( i in obj ) {//遍历json
value = callback.apply( obj[ i ], args ); if ( value === false ) {
break;
}
}
} // A special, fast, case for the most common use of each
} else {
if ( isArray ) {//数组
for ( ; i < length; i++ ) {//回调函数
value = callback.call( obj[ i ], i, obj[ i ] ); if ( value === false ) {//回调函数返回值是false就不循环了
break;
}
}
} else {//json
for ( i in obj ) {
value = callback.call( obj[ i ], i, obj[ i ] ); if ( value === false ) {
break;
}
}
}
} return obj;
}, trim: function( text ) {
return text == null ? "" : core_trim.call( text );
}, // 类似于数组转成真正数组
/*
var s = 123;
console.log( $.makeArray( s ) );//[123]
console.log( $.makeArray( s , {length:3} ) );//Object {3: 123, length: 4} var str = {1:1,2:2};
console.log( $.makeArray( str ) );//[{1:1,2:2}]
console.log( $.makeArray( str , {length:3} ) );//{3:{1:1,2:2},length: 4}
*/
makeArray: function( arr, results ) {
var ret = results || []; if ( arr != null ) {
/*Object()将基本类型转成对应的对象类型。
console.log(Object(123));//Number {[[PrimitiveValue]]: 123}
console.log(Object('abc'));//String {0: "a", 1: "b", 2: "c", length: 3, [[PrimitiveValue]]: "abc"}
console.log(Object(true));//Boolean {[[PrimitiveValue]]: true}
*/
if ( isArraylike( Object(arr) ) ) {//'hello'走这里,
jQuery.merge( ret,
typeof arr === "string" ?
[ arr ] : arr
);
} else {//123走这里
core_push.call( ret, arr );
}
} return ret;
}, /* indexof
var arr = ['a','b','c','d'];
alert( $.inArray( 'w' , arr ) );
core_indexOf = [].indexOf, */
inArray: function( elem, arr, i ) {//i是从哪里开始查
return arr == null ? -1 : core_indexOf.call( arr, elem, i );
}, /*
$.merge(['a','b'],['c','d']);//["a", "b", "c", "d"]
$.merge(['a','b'],{0:'c',1:'d'});//["a", "b", "c", "d"]
$.merge({0:'a',1:'b',length:2},{0:'c',1:'d'});//{0: "a", 1: "b", 2: "c", 3: "d", length: 4}
$.merge({0:'a',1:'b',length:2},['c','d']);// {0: "a", 1: "b", 2: "c", 3: "d", length: 4}
*/
merge: function( first, second ) {
var l = second.length,
i = first.length,
j = 0;
// $.merge( ['a','b'],['c','d'] );
if ( typeof l === "number" ) {//json的length不是number,
for ( ; j < l; j++ ) {
first[ i++ ] = second[ j ];
}
} else {// $.merge( ['a','b'],{0:'c',1:'d'} );
while ( second[j] !== undefined ) {
first[ i++ ] = second[ j++ ];//second的json的属性还必须是0.1.2
}
}
first.length = i;
return first;
}, /*
var arr = [1,2,3,4];
arr = $.grep( arr , function( n , i ){//n是每一项,i是下标
return n>2;
} , true );
console.log( arr );//[1,2]
*/
grep: function( elems, callback, inv ) {
var retVal,
ret = [],
i = 0,
length = elems.length;
inv = !!inv;//2个!就把undefined转成false
for ( ; i < length; i++ ) {
retVal = !!callback( elems[ i ], i );//2个!就把不是布尔类型的转成相应的布尔值
if ( inv !== retVal ) {
ret.push( elems[ i ] );
}
} return ret;
}, /*
var arr = [1,2,3,4];
arr = $.map( arr , function(n){
return [n+1];
} );
console.log( arr );//[2,3,4,5]
*/
map: function( elems, callback, arg ) {
var value,
i = 0,
length = elems.length,
isArray = isArraylike( elems ),
ret = [];
if ( isArray ) {
for ( ; i < length; i++ ) {
value = callback( elems[ i ], i, arg );
if ( value != null ) {
ret[ ret.length ] = value;//末尾追加
}
}
} else {//json
for ( i in elems ) {
value = callback( elems[ i ], i, arg );
if ( value != null ) {
ret[ ret.length ] = value;
}
}
}
// ret = [[2],[3],[4],[5]]
return core_concat.apply( [], ret );//[2,3,4,5]
}, // A global GUID counter for objects
guid: 1, /*
function show(n1,n2){
alert(n1);
alert(n2);
alert(this);
}
show();//window
$.proxy(show,document)(3,4);//改变show的this是document,执行,
$.proxy(show,document,3)(4);//改变show的this是document,执行,
$.proxy(show,document,3)//不执行
*/
proxy: function( fn, context ) {
var tmp, args, proxy;
//var obj = {show : function(){}};
//$(document).click( $.proxy(obj,'show') );//让obj下面的show指向obj
if ( typeof context === "string" ) {
context = fn;//context = obj
fn = tmp;//fn = show
//变成了$.proxy(show,obj),统一参数格式
} if ( !jQuery.isFunction( fn ) ) {
return undefined;
} // 第3个参数3,
args = core_slice.call( arguments, 2 );
proxy = function() {//返回的是函数,后面小括号表示这个函数体执行,
return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) );
//[].slice.call( arguments )把arguments转成真正的数组,arguments是后面的参数
}; // 后期处理事件
proxy.guid = fn.guid = fn.guid || jQuery.guid++; return proxy;
}, // 内部使用,
/*
alert( $('#div1').css('width') );
$('#div1').css('background','yellow');
$('#div1').css(''yellow');
$('#div1').css('width','300px');
$('#div1').css({ background : 'yellow' , width : '300px' });
*/
/*attr: function( 'title', 'hello' ) {
jQuery.access( this, jQuery.attr, 'title', 'hello', arguments.length > 1 );
}
*/
access: function( elems, fn, key, value, chainable, emptyGet, raw ) { //elems=$('#div1'),fn是回调函数,key=background,value=yellow,chainable为真表示设置值为假表示设置值,
var i = 0,
length = elems.length,
bulk = key == null;///没有key就是true, //设置多组值:..css({ background : 'yellow' , width : '300px' })
//是object就是json,
if ( jQuery.type( key ) === "object" ) {
chainable = true;
for ( i in key ) {
jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
}
// 设置一组值:$('#div1').css('width','300px');
} else if ( value !== undefined ) {
chainable = true;///不设置就是undefined
if ( !jQuery.isFunction( value ) ) {
raw = true;
}
if ( bulk ) {///没有key
if ( raw ) {///不是函数
fn.call( elems, value );
fn = null;
} else {///是函数,函数现在不执行,套上一层,里面return时候调用函数
bulk = fn;
fn = function( elem, key, value ) {
return bulk.call( jQuery( elem ), value );
};
}
} if ( fn ) {
for ( ; i < length; i++ ) {
//jQuery.attr()
fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
}
}
} //chainable是真就是设置值,
return chainable ?
elems : // Gets
bulk ?///有没有key值
fn.call( elems ) :///有key就调用
length ? fn( elems[0], key ) : emptyGet;///又要分长度
}, now: Date.now, /*
alert( $('#div1').width() );//display:none还是获取到了,利用的是swap方法
alert( $('#div1').get(0).offsetWidth );//原生的宽度方法,display:none就获取不到了
*/
swap: function( elem, options, callback, args ) {
var ret, name,
old = {};//老的样式 // Remember the old values, and insert the new ones
for ( name in options ) {
old[ name ] = elem.style[ name ];//存老的样式
elem.style[ name ] = options[ name ];//设置新的样式
} ret = callback.apply( elem, args || [] );//调用函数获取想要的属性值 // Revert the old values
for ( name in options ) {
elem.style[ name ] = old[ name ];//样式还原
} return ret;
}
});
}
/* $(function(){}) --> rootjQuery.ready(function(){}) --> $(document).ready(function(){}) --> $().ready(function(){}) --> jQuery.ready.promise().done( function(){} )
-----------> addEventListener --> jQuery.ready() --> readyList.resolveWith( document, [ jQuery ] );
-----------> readyList.promise( obj ) -->
ready: function( fn ) {
jQuery.ready.promise().done( fn );
return this;
}, */
jQuery.ready.promise = function( obj ) {
if ( !readyList ) {//是否加载完,刚开始是false,只走进来一次, readyList = jQuery.Deferred(); if ( document.readyState === "complete" ) {//dom加载完document的属性是完成状态,
// Handle it asynchronously to allow scripts the opportunity to delay ready
setTimeout( jQuery.ready ); } else { /*匿名函数全局作用域,dom加载完成执行的方法
completed = function() {
document.removeEventListener( "DOMContentLoaded", completed, false );
window.removeEventListener( "load", completed, false );
jQuery.ready();
jQuery.ready() --> readyList.resolveWith( document, [ jQuery ] );
};
*/
document.addEventListener( "DOMContentLoaded", completed, false ); // A fallback to window.onload, that will always work
window.addEventListener( "load", completed, false );
}
}
return readyList.promise( obj );
}; // Populate the class2type map
jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
class2type[ "[object " + name + "]" ] = name.toLowerCase();
}); //是不是数组,特殊数组,带length属性的json
function isArraylike( obj ) {
var length = obj.length,
type = jQuery.type( obj ); if ( jQuery.isWindow( obj ) ) {//是不是window,下面的判断有可能window也满足,
return false;
} if ( obj.nodeType === 1 && length ) {//一组元素节点是类数组
return true;
}
//type !== "function"不加这个,那么有length属性的函数就进去了
return type === "array" || type !== "function" &&
( length === 0 ||
typeof length === "number" && length > 0 && ( length - 1 ) in obj );
} // All jQuery objects should point back to these
rootjQuery = jQuery(document);
/////////////////////////////////////////Sizzle BEGIN////////////////////////
{
/*!
* Sizzle CSS Selector Engine v1.9.4-pre
* http://sizzlejs.com/
*
* Copyright 2013 jQuery Foundation, Inc. and other contributors
* Released under the MIT license
* http://jquery.org/license
*
* Date: 2013-06-03
*/
(function( window, undefined ) { var i,
support,
cachedruns,
Expr,
getText,
isXML,
compile,
outermostContext,
sortInput, // Local document vars
setDocument,
document,
docElem,
documentIsHTML,
rbuggyQSA,
rbuggyMatches,
matches,
contains, // Instance-specific data
expando = "sizzle" + -(new Date()),
preferredDoc = window.document,
dirruns = 0,
done = 0,
classCache = createCache(),
tokenCache = createCache(),
compilerCache = createCache(),
hasDuplicate = false,
sortOrder = function( a, b ) {
if ( a === b ) {
hasDuplicate = true;
return 0;
}
return 0;
}, // General-purpose constants
strundefined = typeof undefined,
MAX_NEGATIVE = 1 << 31, // Instance methods
hasOwn = ({}).hasOwnProperty,
arr = [],
pop = arr.pop,
push_native = arr.push,
push = arr.push,
slice = arr.slice,
// Use a stripped-down indexOf if we can't use a native one
indexOf = arr.indexOf || function( elem ) {
var i = 0,
len = this.length;
for ( ; i < len; i++ ) {
if ( this[i] === elem ) {
return i;
}
}
return -1;
}, booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", // Regular expressions // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
whitespace = "[\\x20\\t\\r\\n\\f]",
// http://www.w3.org/TR/css3-syntax/#characters
characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", // Loosely modeled on CSS identifier characters
// An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
// Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
identifier = characterEncoding.replace( "w", "w#" ), // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors
attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
"*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", // Prefer arguments quoted,
// then not containing pseudos/brackets,
// then attribute selectors/non-parenthetical expressions,
// then anything else
// These preferences are here to reduce the number of selectors
// needing tokenize in the PSEUDO preFilter
pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)", // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), rsibling = new RegExp( whitespace + "*[+~]" ),
rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*)" + whitespace + "*\\]", "g" ), rpseudo = new RegExp( pseudos ),
ridentifier = new RegExp( "^" + identifier + "$" ), matchExpr = {
"ID": new RegExp( "^#(" + characterEncoding + ")" ),
"CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
"TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
"ATTR": new RegExp( "^" + attributes ),
"PSEUDO": new RegExp( "^" + pseudos ),
"CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
"*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
"*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
"bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
// For use in libraries implementing .is()
// We use this for POS matching in `select`
"needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
}, rnative = /^[^{]+\{\s*\[native \w/, // Easily-parseable/retrievable ID or TAG or CLASS selectors
rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, rinputs = /^(?:input|select|textarea|button)$/i,
rheader = /^h\d$/i, rescape = /'|\\/g, // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
funescape = function( _, escaped, escapedWhitespace ) {
var high = "0x" + escaped - 0x10000;
// NaN means non-codepoint
// Support: Firefox
// Workaround erroneous numeric interpretation of +"0x"
return high !== high || escapedWhitespace ?
escaped :
// BMP codepoint
high < 0 ?
String.fromCharCode( high + 0x10000 ) :
// Supplemental Plane codepoint (surrogate pair)
String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
}; // Optimize for push.apply( _, NodeList )
try {
push.apply(
(arr = slice.call( preferredDoc.childNodes )),
preferredDoc.childNodes
);
// Support: Android<4.0
// Detect silently failing push.apply
arr[ preferredDoc.childNodes.length ].nodeType;
} catch ( e ) {
push = { apply: arr.length ? // Leverage slice if possible
function( target, els ) {
push_native.apply( target, slice.call(els) );
} : // Support: IE<9
// Otherwise append directly
function( target, els ) {
var j = target.length,
i = 0;
// Can't trust NodeList.length
while ( (target[j++] = els[i++]) ) {}
target.length = j - 1;
}
};
} function Sizzle( selector, context, results, seed ) {
var match, elem, m, nodeType,
// QSA vars
i, groups, old, nid, newContext, newSelector; if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
setDocument( context );
} context = context || document;
results = results || []; if ( !selector || typeof selector !== "string" ) {
return results;
} if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
return [];
} if ( documentIsHTML && !seed ) { // Shortcuts
if ( (match = rquickExpr.exec( selector )) ) {
// Speed-up: Sizzle("#ID")
if ( (m = match[1]) ) {
if ( nodeType === 9 ) {
elem = context.getElementById( m );
// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document #6963
if ( elem && elem.parentNode ) {
// Handle the case where IE, Opera, and Webkit return items
// by name instead of ID
if ( elem.id === m ) {
results.push( elem );
return results;
}
} else {
return results;
}
} else {
// Context is not a document
if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
contains( context, elem ) && elem.id === m ) {
results.push( elem );
return results;
}
} // Speed-up: Sizzle("TAG")
} else if ( match[2] ) {
push.apply( results, context.getElementsByTagName( selector ) );
return results; // Speed-up: Sizzle(".CLASS")
} else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) {
push.apply( results, context.getElementsByClassName( m ) );
return results;
}
} // QSA path
if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
nid = old = expando;
newContext = context;
newSelector = nodeType === 9 && selector; // qSA works strangely on Element-rooted queries
// We can work around this by specifying an extra ID on the root
// and working up from there (Thanks to Andrew Dupont for the technique)
// IE 8 doesn't work on object elements
if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
groups = tokenize( selector ); if ( (old = context.getAttribute("id")) ) {
nid = old.replace( rescape, "\\$&" );
} else {
context.setAttribute( "id", nid );
}
nid = "[id='" + nid + "'] "; i = groups.length;
while ( i-- ) {
groups[i] = nid + toSelector( groups[i] );
}
newContext = rsibling.test( selector ) && context.parentNode || context;
newSelector = groups.join(",");
} if ( newSelector ) {
try {
push.apply( results,
newContext.querySelectorAll( newSelector )
);
return results;
} catch(qsaError) {
} finally {
if ( !old ) {
context.removeAttribute("id");
}
}
}
}
} // All others
return select( selector.replace( rtrim, "$1" ), context, results, seed );
} /**
* Create key-value caches of limited size
* @returns {Function(string, Object)} Returns the Object data after storing it on itself with
* property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
* deleting the oldest entry
*/
function createCache() {
var keys = []; function cache( key, value ) {
// Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
if ( keys.push( key += " " ) > Expr.cacheLength ) {
// Only keep the most recent entries
delete cache[ keys.shift() ];
}
return (cache[ key ] = value);
}
return cache;
} /**
* Mark a function for special use by Sizzle
* @param {Function} fn The function to mark
*/
function markFunction( fn ) {
fn[ expando ] = true;
return fn;
} /**
* Support testing using an element
* @param {Function} fn Passed the created div and expects a boolean result
*/
function assert( fn ) {
var div = document.createElement("div"); try {
return !!fn( div );
} catch (e) {
return false;
} finally {
// Remove from its parent by default
if ( div.parentNode ) {
div.parentNode.removeChild( div );
}
// release memory in IE
div = null;
}
} /**
* Adds the same handler for all of the specified attrs
* @param {String} attrs Pipe-separated list of attributes
* @param {Function} handler The method that will be applied
*/
function addHandle( attrs, handler ) {
var arr = attrs.split("|"),
i = attrs.length; while ( i-- ) {
Expr.attrHandle[ arr[i] ] = handler;
}
} /**
* Checks document order of two siblings
* @param {Element} a
* @param {Element} b
* @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
*/
function siblingCheck( a, b ) {
var cur = b && a,
diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
( ~b.sourceIndex || MAX_NEGATIVE ) -
( ~a.sourceIndex || MAX_NEGATIVE ); // Use IE sourceIndex if available on both nodes
if ( diff ) {
return diff;
} // Check if b follows a
if ( cur ) {
while ( (cur = cur.nextSibling) ) {
if ( cur === b ) {
return -1;
}
}
} return a ? 1 : -1;
} /**
* Returns a function to use in pseudos for input types
* @param {String} type
*/
function createInputPseudo( type ) {
return function( elem ) {
var name = elem.nodeName.toLowerCase();
return name === "input" && elem.type === type;
};
} /**
* Returns a function to use in pseudos for buttons
* @param {String} type
*/
function createButtonPseudo( type ) {
return function( elem ) {
var name = elem.nodeName.toLowerCase();
return (name === "input" || name === "button") && elem.type === type;
};
} /**
* Returns a function to use in pseudos for positionals
* @param {Function} fn
*/
function createPositionalPseudo( fn ) {
return markFunction(function( argument ) {
argument = +argument;
return markFunction(function( seed, matches ) {
var j,
matchIndexes = fn( [], seed.length, argument ),
i = matchIndexes.length; // Match elements found at the specified indexes
while ( i-- ) {
if ( seed[ (j = matchIndexes[i]) ] ) {
seed[j] = !(matches[j] = seed[j]);
}
}
});
});
} /**
* Detect xml
* @param {Element|Object} elem An element or a document
*/
isXML = Sizzle.isXML = function( elem ) {
// documentElement is verified for cases where it doesn't yet exist
// (such as loading iframes in IE - #4833)
var documentElement = elem && (elem.ownerDocument || elem).documentElement;
return documentElement ? documentElement.nodeName !== "HTML" : false;
}; // Expose support vars for convenience
support = Sizzle.support = {}; /**
* Sets document-related variables once based on the current document
* @param {Element|Object} [doc] An element or document object to use to set the document
* @returns {Object} Returns the current document
*/
setDocument = Sizzle.setDocument = function( node ) {
var doc = node ? node.ownerDocument || node : preferredDoc,
parent = doc.defaultView; // If no document and documentElement is available, return
if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
return document;
} // Set our document
document = doc;
docElem = doc.documentElement; // Support tests
documentIsHTML = !isXML( doc ); // Support: IE>8
// If iframe document is assigned to "document" variable and if iframe has been reloaded,
// IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936
// IE6-8 do not support the defaultView property so parent will be undefined
if ( parent && parent.attachEvent && parent !== parent.top ) {
parent.attachEvent( "onbeforeunload", function() {
setDocument();
});
} /* Attributes
---------------------------------------------------------------------- */ // Support: IE<8
// Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans)
support.attributes = assert(function( div ) {
div.className = "i";
return !div.getAttribute("className");
}); /* getElement(s)By*
---------------------------------------------------------------------- */ // Check if getElementsByTagName("*") returns only elements
support.getElementsByTagName = assert(function( div ) {
div.appendChild( doc.createComment("") );
return !div.getElementsByTagName("*").length;
}); // Check if getElementsByClassName can be trusted
support.getElementsByClassName = assert(function( div ) {
div.innerHTML = "<div class='a'></div><div class='a i'></div>"; // Support: Safari<4
// Catch class over-caching
div.firstChild.className = "i";
// Support: Opera<10
// Catch gEBCN failure to find non-leading classes
return div.getElementsByClassName("i").length === 2;
}); // Support: IE<10
// Check if getElementById returns elements by name
// The broken getElementById methods don't pick up programatically-set names,
// so use a roundabout getElementsByName test
support.getById = assert(function( div ) {
docElem.appendChild( div ).id = expando;
return !doc.getElementsByName || !doc.getElementsByName( expando ).length;
}); // ID find and filter
if ( support.getById ) {
Expr.find["ID"] = function( id, context ) {
if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
var m = context.getElementById( id );
// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document #6963
return m && m.parentNode ? [m] : [];
}
};
Expr.filter["ID"] = function( id ) {
var attrId = id.replace( runescape, funescape );
return function( elem ) {
return elem.getAttribute("id") === attrId;
};
};
} else {
// Support: IE6/7
// getElementById is not reliable as a find shortcut
delete Expr.find["ID"]; Expr.filter["ID"] = function( id ) {
var attrId = id.replace( runescape, funescape );
return function( elem ) {
var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
return node && node.value === attrId;
};
};
} // Tag
Expr.find["TAG"] = support.getElementsByTagName ?
function( tag, context ) {
if ( typeof context.getElementsByTagName !== strundefined ) {
return context.getElementsByTagName( tag );
}
} :
function( tag, context ) {
var elem,
tmp = [],
i = 0,
results = context.getElementsByTagName( tag ); // Filter out possible comments
if ( tag === "*" ) {
while ( (elem = results[i++]) ) {
if ( elem.nodeType === 1 ) {
tmp.push( elem );
}
} return tmp;
}
return results;
}; // Class
Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) {
return context.getElementsByClassName( className );
}
}; /* QSA/matchesSelector
---------------------------------------------------------------------- */ // QSA and matchesSelector support // matchesSelector(:active) reports false when true (IE9/Opera 11.5)
rbuggyMatches = []; // qSa(:focus) reports false when true (Chrome 21)
// We allow this because of a bug in IE8/9 that throws an error
// whenever `document.activeElement` is accessed on an iframe
// So, we allow :focus to pass through QSA all the time to avoid the IE error
// See http://bugs.jquery.com/ticket/13378
rbuggyQSA = []; if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) {
// Build QSA regex
// Regex strategy adopted from Diego Perini
assert(function( div ) {
// Select is set to empty string on purpose
// This is to test IE's treatment of not explicitly
// setting a boolean content attribute,
// since its presence should be enough
// http://bugs.jquery.com/ticket/12359
div.innerHTML = "<select><option selected=''></option></select>"; // Support: IE8
// Boolean attributes and "value" are not treated correctly
if ( !div.querySelectorAll("[selected]").length ) {
rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
} // Webkit/Opera - :checked should return selected option elements
// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
// IE8 throws error here and will not see later tests
if ( !div.querySelectorAll(":checked").length ) {
rbuggyQSA.push(":checked");
}
}); assert(function( div ) { // Support: Opera 10-12/IE8
// ^= $= *= and empty values
// Should not select anything
// Support: Windows 8 Native Apps
// The type attribute is restricted during .innerHTML assignment
var input = doc.createElement("input");
input.setAttribute( "type", "hidden" );
div.appendChild( input ).setAttribute( "t", "" ); if ( div.querySelectorAll("[t^='']").length ) {
rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
} // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
// IE8 throws error here and will not see later tests
if ( !div.querySelectorAll(":enabled").length ) {
rbuggyQSA.push( ":enabled", ":disabled" );
} // Opera 10-11 does not throw on post-comma invalid pseudos
div.querySelectorAll("*,:x");
rbuggyQSA.push(",.*:");
});
} if ( (support.matchesSelector = rnative.test( (matches = docElem.webkitMatchesSelector ||
docElem.mozMatchesSelector ||
docElem.oMatchesSelector ||
docElem.msMatchesSelector) )) ) { assert(function( div ) {
// Check to see if it's possible to do matchesSelector
// on a disconnected node (IE 9)
support.disconnectedMatch = matches.call( div, "div" ); // This should fail with an exception
// Gecko does not error, returns false instead
matches.call( div, "[s!='']:x" );
rbuggyMatches.push( "!=", pseudos );
});
} rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); /* Contains
---------------------------------------------------------------------- */ // Element contains another
// Purposefully does not implement inclusive descendent
// As in, an element does not contain itself
contains = rnative.test( docElem.contains ) || docElem.compareDocumentPosition ?
function( a, b ) {
var adown = a.nodeType === 9 ? a.documentElement : a,
bup = b && b.parentNode;
return a === bup || !!( bup && bup.nodeType === 1 && (
adown.contains ?
adown.contains( bup ) :
a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
));
} :
function( a, b ) {
if ( b ) {
while ( (b = b.parentNode) ) {
if ( b === a ) {
return true;
}
}
}
return false;
}; /* Sorting
---------------------------------------------------------------------- */ // Document order sorting
sortOrder = docElem.compareDocumentPosition ?
function( a, b ) { // Flag for duplicate removal
if ( a === b ) {
hasDuplicate = true;
return 0;
} var compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b ); if ( compare ) {
// Disconnected nodes
if ( compare & 1 ||
(!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { // Choose the first element that is related to our preferred document
if ( a === doc || contains(preferredDoc, a) ) {
return -1;
}
if ( b === doc || contains(preferredDoc, b) ) {
return 1;
} // Maintain original order
return sortInput ?
( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
0;
} return compare & 4 ? -1 : 1;
} // Not directly comparable, sort on existence of method
return a.compareDocumentPosition ? -1 : 1;
} :
function( a, b ) {
var cur,
i = 0,
aup = a.parentNode,
bup = b.parentNode,
ap = [ a ],
bp = [ b ]; // Exit early if the nodes are identical
if ( a === b ) {
hasDuplicate = true;
return 0; // Parentless nodes are either documents or disconnected
} else if ( !aup || !bup ) {
return a === doc ? -1 :
b === doc ? 1 :
aup ? -1 :
bup ? 1 :
sortInput ?
( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
0; // If the nodes are siblings, we can do a quick check
} else if ( aup === bup ) {
return siblingCheck( a, b );
} // Otherwise we need full lists of their ancestors for comparison
cur = a;
while ( (cur = cur.parentNode) ) {
ap.unshift( cur );
}
cur = b;
while ( (cur = cur.parentNode) ) {
bp.unshift( cur );
} // Walk down the tree looking for a discrepancy
while ( ap[i] === bp[i] ) {
i++;
} return i ?
// Do a sibling check if the nodes have a common ancestor
siblingCheck( ap[i], bp[i] ) : // Otherwise nodes in our document sort first
ap[i] === preferredDoc ? -1 :
bp[i] === preferredDoc ? 1 :
0;
}; return doc;
}; Sizzle.matches = function( expr, elements ) {
return Sizzle( expr, null, null, elements );
}; Sizzle.matchesSelector = function( elem, expr ) {
// Set document vars if needed
if ( ( elem.ownerDocument || elem ) !== document ) {
setDocument( elem );
} // Make sure that attribute selectors are quoted
expr = expr.replace( rattributeQuotes, "='$1']" ); if ( support.matchesSelector && documentIsHTML &&
( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { try {
var ret = matches.call( elem, expr ); // IE 9's matchesSelector returns false on disconnected nodes
if ( ret || support.disconnectedMatch ||
// As well, disconnected nodes are said to be in a document
// fragment in IE 9
elem.document && elem.document.nodeType !== 11 ) {
return ret;
}
} catch(e) {}
} return Sizzle( expr, document, null, [elem] ).length > 0;
}; Sizzle.contains = function( context, elem ) {
// Set document vars if needed
if ( ( context.ownerDocument || context ) !== document ) {
setDocument( context );
}
return contains( context, elem );
}; Sizzle.attr = function( elem, name ) {
// Set document vars if needed
if ( ( elem.ownerDocument || elem ) !== document ) {
setDocument( elem );
} var fn = Expr.attrHandle[ name.toLowerCase() ],
// Don't get fooled by Object.prototype properties (jQuery #13807)
val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
fn( elem, name, !documentIsHTML ) :
undefined; return val === undefined ?
support.attributes || !documentIsHTML ?
elem.getAttribute( name ) :
(val = elem.getAttributeNode(name)) && val.specified ?
val.value :
null :
val;
}; Sizzle.error = function( msg ) {
throw new Error( "Syntax error, unrecognized expression: " + msg );
}; /**
* Document sorting and removing duplicates
* @param {ArrayLike} results
*/
Sizzle.uniqueSort = function( results ) {
var elem,
duplicates = [],
j = 0,
i = 0; // Unless we *know* we can detect duplicates, assume their presence
hasDuplicate = !support.detectDuplicates;
sortInput = !support.sortStable && results.slice( 0 );
results.sort( sortOrder ); if ( hasDuplicate ) {
while ( (elem = results[i++]) ) {
if ( elem === results[ i ] ) {
j = duplicates.push( i );
}
}
while ( j-- ) {
results.splice( duplicates[ j ], 1 );
}
} return results;
}; /**
* Utility function for retrieving the text value of an array of DOM nodes
* @param {Array|Element} elem
*/
getText = Sizzle.getText = function( elem ) {
var node,
ret = "",
i = 0,
nodeType = elem.nodeType; if ( !nodeType ) {
// If no nodeType, this is expected to be an array
for ( ; (node = elem[i]); i++ ) {
// Do not traverse comment nodes
ret += getText( node );
}
} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
// Use textContent for elements
// innerText usage removed for consistency of new lines (see #11153)
if ( typeof elem.textContent === "string" ) {
return elem.textContent;
} else {
// Traverse its children
for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
ret += getText( elem );
}
}
} else if ( nodeType === 3 || nodeType === 4 ) {
return elem.nodeValue;
}
// Do not include comment or processing instruction nodes return ret;
}; Expr = Sizzle.selectors = { // Can be adjusted by the user
cacheLength: 50, createPseudo: markFunction, match: matchExpr, attrHandle: {}, find: {}, relative: {
">": { dir: "parentNode", first: true },
" ": { dir: "parentNode" },
"+": { dir: "previousSibling", first: true },
"~": { dir: "previousSibling" }
}, preFilter: {
"ATTR": function( match ) {
match[1] = match[1].replace( runescape, funescape ); // Move the given value to match[3] whether quoted or unquoted
match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape ); if ( match[2] === "~=" ) {
match[3] = " " + match[3] + " ";
} return match.slice( 0, 4 );
}, "CHILD": function( match ) {
/* matches from matchExpr["CHILD"]
1 type (only|nth|...)
2 what (child|of-type)
3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
4 xn-component of xn+y argument ([+-]?\d*n|)
5 sign of xn-component
6 x of xn-component
7 sign of y-component
8 y of y-component
*/
match[1] = match[1].toLowerCase(); if ( match[1].slice( 0, 3 ) === "nth" ) {
// nth-* requires argument
if ( !match[3] ) {
Sizzle.error( match[0] );
} // numeric x and y parameters for Expr.filter.CHILD
// remember that false/true cast respectively to 0/1
match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); // other types prohibit arguments
} else if ( match[3] ) {
Sizzle.error( match[0] );
} return match;
}, "PSEUDO": function( match ) {
var excess,
unquoted = !match[5] && match[2]; if ( matchExpr["CHILD"].test( match[0] ) ) {
return null;
} // Accept quoted arguments as-is
if ( match[3] && match[4] !== undefined ) {
match[2] = match[4]; // Strip excess characters from unquoted arguments
} else if ( unquoted && rpseudo.test( unquoted ) &&
// Get excess from tokenize (recursively)
(excess = tokenize( unquoted, true )) &&
// advance to the next closing parenthesis
(excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { // excess is a negative index
match[0] = match[0].slice( 0, excess );
match[2] = unquoted.slice( 0, excess );
} // Return only captures needed by the pseudo filter method (type and argument)
return match.slice( 0, 3 );
}
}, filter: { "TAG": function( nodeNameSelector ) {
var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
return nodeNameSelector === "*" ?
function() { return true; } :
function( elem ) {
return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
};
}, "CLASS": function( className ) {
var pattern = classCache[ className + " " ]; return pattern ||
(pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
classCache( className, function( elem ) {
return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" );
});
}, "ATTR": function( name, operator, check ) {
return function( elem ) {
var result = Sizzle.attr( elem, name ); if ( result == null ) {
return operator === "!=";
}
if ( !operator ) {
return true;
} result += ""; return operator === "=" ? result === check :
operator === "!=" ? result !== check :
operator === "^=" ? check && result.indexOf( check ) === 0 :
operator === "*=" ? check && result.indexOf( check ) > -1 :
operator === "$=" ? check && result.slice( -check.length ) === check :
operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
false;
};
}, "CHILD": function( type, what, argument, first, last ) {
var simple = type.slice( 0, 3 ) !== "nth",
forward = type.slice( -4 ) !== "last",
ofType = what === "of-type"; return first === 1 && last === 0 ? // Shortcut for :nth-*(n)
function( elem ) {
return !!elem.parentNode;
} : function( elem, context, xml ) {
var cache, outerCache, node, diff, nodeIndex, start,
dir = simple !== forward ? "nextSibling" : "previousSibling",
parent = elem.parentNode,
name = ofType && elem.nodeName.toLowerCase(),
useCache = !xml && !ofType; if ( parent ) { // :(first|last|only)-(child|of-type)
if ( simple ) {
while ( dir ) {
node = elem;
while ( (node = node[ dir ]) ) {
if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
return false;
}
}
// Reverse direction for :only-* (if we haven't yet done so)
start = dir = type === "only" && !start && "nextSibling";
}
return true;
} start = [ forward ? parent.firstChild : parent.lastChild ]; // non-xml :nth-child(...) stores cache data on `parent`
if ( forward && useCache ) {
// Seek `elem` from a previously-cached index
outerCache = parent[ expando ] || (parent[ expando ] = {});
cache = outerCache[ type ] || [];
nodeIndex = cache[0] === dirruns && cache[1];
diff = cache[0] === dirruns && cache[2];
node = nodeIndex && parent.childNodes[ nodeIndex ]; while ( (node = ++nodeIndex && node && node[ dir ] || // Fallback to seeking `elem` from the start
(diff = nodeIndex = 0) || start.pop()) ) { // When found, cache indexes on `parent` and break
if ( node.nodeType === 1 && ++diff && node === elem ) {
outerCache[ type ] = [ dirruns, nodeIndex, diff ];
break;
}
} // Use previously-cached element index if available
} else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
diff = cache[1]; // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
} else {
// Use the same loop as above to seek `elem` from the start
while ( (node = ++nodeIndex && node && node[ dir ] ||
(diff = nodeIndex = 0) || start.pop()) ) { if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
// Cache the index of each encountered element
if ( useCache ) {
(node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
} if ( node === elem ) {
break;
}
}
}
} // Incorporate the offset, then check against cycle size
diff -= last;
return diff === first || ( diff % first === 0 && diff / first >= 0 );
}
};
}, "PSEUDO": function( pseudo, argument ) {
// pseudo-class names are case-insensitive
// http://www.w3.org/TR/selectors/#pseudo-classes
// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
// Remember that setFilters inherits from pseudos
var args,
fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
Sizzle.error( "unsupported pseudo: " + pseudo ); // The user may use createPseudo to indicate that
// arguments are needed to create the filter function
// just as Sizzle does
if ( fn[ expando ] ) {
return fn( argument );
} // But maintain support for old signatures
if ( fn.length > 1 ) {
args = [ pseudo, pseudo, "", argument ];
return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
markFunction(function( seed, matches ) {
var idx,
matched = fn( seed, argument ),
i = matched.length;
while ( i-- ) {
idx = indexOf.call( seed, matched[i] );
seed[ idx ] = !( matches[ idx ] = matched[i] );
}
}) :
function( elem ) {
return fn( elem, 0, args );
};
} return fn;
}
}, pseudos: {
// Potentially complex pseudos
"not": markFunction(function( selector ) {
// Trim the selector passed to compile
// to avoid treating leading and trailing
// spaces as combinators
var input = [],
results = [],
matcher = compile( selector.replace( rtrim, "$1" ) ); return matcher[ expando ] ?
markFunction(function( seed, matches, context, xml ) {
var elem,
unmatched = matcher( seed, null, xml, [] ),
i = seed.length; // Match elements unmatched by `matcher`
while ( i-- ) {
if ( (elem = unmatched[i]) ) {
seed[i] = !(matches[i] = elem);
}
}
}) :
function( elem, context, xml ) {
input[0] = elem;
matcher( input, null, xml, results );
return !results.pop();
};
}), "has": markFunction(function( selector ) {
return function( elem ) {
return Sizzle( selector, elem ).length > 0;
};
}), "contains": markFunction(function( text ) {
return function( elem ) {
return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
};
}), // "Whether an element is represented by a :lang() selector
// is based solely on the element's language value
// being equal to the identifier C,
// or beginning with the identifier C immediately followed by "-".
// The matching of C against the element's language value is performed case-insensitively.
// The identifier C does not have to be a valid language name."
// http://www.w3.org/TR/selectors/#lang-pseudo
"lang": markFunction( function( lang ) {
// lang value must be a valid identifier
if ( !ridentifier.test(lang || "") ) {
Sizzle.error( "unsupported lang: " + lang );
}
lang = lang.replace( runescape, funescape ).toLowerCase();
return function( elem ) {
var elemLang;
do {
if ( (elemLang = documentIsHTML ?
elem.lang :
elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { elemLang = elemLang.toLowerCase();
return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
}
} while ( (elem = elem.parentNode) && elem.nodeType === 1 );
return false;
};
}), // Miscellaneous
"target": function( elem ) {
var hash = window.location && window.location.hash;
return hash && hash.slice( 1 ) === elem.id;
}, "root": function( elem ) {
return elem === docElem;
}, "focus": function( elem ) {
return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
}, // Boolean properties
"enabled": function( elem ) {
return elem.disabled === false;
}, "disabled": function( elem ) {
return elem.disabled === true;
}, "checked": function( elem ) {
// In CSS3, :checked should return both checked and selected elements
// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
var nodeName = elem.nodeName.toLowerCase();
return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
}, "selected": function( elem ) {
// Accessing this property makes selected-by-default
// options in Safari work properly
if ( elem.parentNode ) {
elem.parentNode.selectedIndex;
} return elem.selected === true;
}, // Contents
"empty": function( elem ) {
// http://www.w3.org/TR/selectors/#empty-pseudo
// :empty is only affected by element nodes and content nodes(including text(3), cdata(4)),
// not comment, processing instructions, or others
// Thanks to Diego Perini for the nodeName shortcut
// Greater than "@" means alpha characters (specifically not starting with "#" or "?")
for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) {
return false;
}
}
return true;
}, "parent": function( elem ) {
return !Expr.pseudos["empty"]( elem );
}, // Element/input types
"header": function( elem ) {
return rheader.test( elem.nodeName );
}, "input": function( elem ) {
return rinputs.test( elem.nodeName );
}, "button": function( elem ) {
var name = elem.nodeName.toLowerCase();
return name === "input" && elem.type === "button" || name === "button";
}, "text": function( elem ) {
var attr;
// IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
// use getAttribute instead to test this case
return elem.nodeName.toLowerCase() === "input" &&
elem.type === "text" &&
( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type );
}, // Position-in-collection
"first": createPositionalPseudo(function() {
return [ 0 ];
}), "last": createPositionalPseudo(function( matchIndexes, length ) {
return [ length - 1 ];
}), "eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
return [ argument < 0 ? argument + length : argument ];
}), "even": createPositionalPseudo(function( matchIndexes, length ) {
var i = 0;
for ( ; i < length; i += 2 ) {
matchIndexes.push( i );
}
return matchIndexes;
}), "odd": createPositionalPseudo(function( matchIndexes, length ) {
var i = 1;
for ( ; i < length; i += 2 ) {
matchIndexes.push( i );
}
return matchIndexes;
}), "lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
var i = argument < 0 ? argument + length : argument;
for ( ; --i >= 0; ) {
matchIndexes.push( i );
}
return matchIndexes;
}), "gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
var i = argument < 0 ? argument + length : argument;
for ( ; ++i < length; ) {
matchIndexes.push( i );
}
return matchIndexes;
})
}
}; Expr.pseudos["nth"] = Expr.pseudos["eq"]; // Add button/input type pseudos
for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
Expr.pseudos[ i ] = createInputPseudo( i );
}
for ( i in { submit: true, reset: true } ) {
Expr.pseudos[ i ] = createButtonPseudo( i );
} // Easy API for creating new setFilters
function setFilters() {}
setFilters.prototype = Expr.filters = Expr.pseudos;
Expr.setFilters = new setFilters(); function tokenize( selector, parseOnly ) {
var matched, match, tokens, type,
soFar, groups, preFilters,
cached = tokenCache[ selector + " " ]; if ( cached ) {
return parseOnly ? 0 : cached.slice( 0 );
} soFar = selector;
groups = [];
preFilters = Expr.preFilter; while ( soFar ) { // Comma and first run
if ( !matched || (match = rcomma.exec( soFar )) ) {
if ( match ) {
// Don't consume trailing commas as valid
soFar = soFar.slice( match[0].length ) || soFar;
}
groups.push( tokens = [] );
} matched = false; // Combinators
if ( (match = rcombinators.exec( soFar )) ) {
matched = match.shift();
tokens.push({
value: matched,
// Cast descendant combinators to space
type: match[0].replace( rtrim, " " )
});
soFar = soFar.slice( matched.length );
} // Filters
for ( type in Expr.filter ) {
if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
(match = preFilters[ type ]( match ))) ) {
matched = match.shift();
tokens.push({
value: matched,
type: type,
matches: match
});
soFar = soFar.slice( matched.length );
}
} if ( !matched ) {
break;
}
} // Return the length of the invalid excess
// if we're just parsing
// Otherwise, throw an error or return tokens
return parseOnly ?
soFar.length :
soFar ?
Sizzle.error( selector ) :
// Cache the tokens
tokenCache( selector, groups ).slice( 0 );
} function toSelector( tokens ) {
var i = 0,
len = tokens.length,
selector = "";
for ( ; i < len; i++ ) {
selector += tokens[i].value;
}
return selector;
} function addCombinator( matcher, combinator, base ) {
var dir = combinator.dir,
checkNonElements = base && dir === "parentNode",
doneName = done++; return combinator.first ?
// Check against closest ancestor/preceding element
function( elem, context, xml ) {
while ( (elem = elem[ dir ]) ) {
if ( elem.nodeType === 1 || checkNonElements ) {
return matcher( elem, context, xml );
}
}
} : // Check against all ancestor/preceding elements
function( elem, context, xml ) {
var data, cache, outerCache,
dirkey = dirruns + " " + doneName; // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
if ( xml ) {
while ( (elem = elem[ dir ]) ) {
if ( elem.nodeType === 1 || checkNonElements ) {
if ( matcher( elem, context, xml ) ) {
return true;
}
}
}
} else {
while ( (elem = elem[ dir ]) ) {
if ( elem.nodeType === 1 || checkNonElements ) {
outerCache = elem[ expando ] || (elem[ expando ] = {});
if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) {
if ( (data = cache[1]) === true || data === cachedruns ) {
return data === true;
}
} else {
cache = outerCache[ dir ] = [ dirkey ];
cache[1] = matcher( elem, context, xml ) || cachedruns;
if ( cache[1] === true ) {
return true;
}
}
}
}
}
};
} function elementMatcher( matchers ) {
return matchers.length > 1 ?
function( elem, context, xml ) {
var i = matchers.length;
while ( i-- ) {
if ( !matchers[i]( elem, context, xml ) ) {
return false;
}
}
return true;
} :
matchers[0];
} function condense( unmatched, map, filter, context, xml ) {
var elem,
newUnmatched = [],
i = 0,
len = unmatched.length,
mapped = map != null; for ( ; i < len; i++ ) {
if ( (elem = unmatched[i]) ) {
if ( !filter || filter( elem, context, xml ) ) {
newUnmatched.push( elem );
if ( mapped ) {
map.push( i );
}
}
}
} return newUnmatched;
} function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
if ( postFilter && !postFilter[ expando ] ) {
postFilter = setMatcher( postFilter );
}
if ( postFinder && !postFinder[ expando ] ) {
postFinder = setMatcher( postFinder, postSelector );
}
return markFunction(function( seed, results, context, xml ) {
var temp, i, elem,
preMap = [],
postMap = [],
preexisting = results.length, // Get initial elements from seed or context
elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), // Prefilter to get matcher input, preserving a map for seed-results synchronization
matcherIn = preFilter && ( seed || !selector ) ?
condense( elems, preMap, preFilter, context, xml ) :
elems, matcherOut = matcher ?
// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
postFinder || ( seed ? preFilter : preexisting || postFilter ) ? // ...intermediate processing is necessary
[] : // ...otherwise use results directly
results :
matcherIn; // Find primary matches
if ( matcher ) {
matcher( matcherIn, matcherOut, context, xml );
} // Apply postFilter
if ( postFilter ) {
temp = condense( matcherOut, postMap );
postFilter( temp, [], context, xml ); // Un-match failing elements by moving them back to matcherIn
i = temp.length;
while ( i-- ) {
if ( (elem = temp[i]) ) {
matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
}
}
} if ( seed ) {
if ( postFinder || preFilter ) {
if ( postFinder ) {
// Get the final matcherOut by condensing this intermediate into postFinder contexts
temp = [];
i = matcherOut.length;
while ( i-- ) {
if ( (elem = matcherOut[i]) ) {
// Restore matcherIn since elem is not yet a final match
temp.push( (matcherIn[i] = elem) );
}
}
postFinder( null, (matcherOut = []), temp, xml );
} // Move matched elements from seed to results to keep them synchronized
i = matcherOut.length;
while ( i-- ) {
if ( (elem = matcherOut[i]) &&
(temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { seed[temp] = !(results[temp] = elem);
}
}
} // Add elements to results, through postFinder if defined
} else {
matcherOut = condense(
matcherOut === results ?
matcherOut.splice( preexisting, matcherOut.length ) :
matcherOut
);
if ( postFinder ) {
postFinder( null, results, matcherOut, xml );
} else {
push.apply( results, matcherOut );
}
}
});
} function matcherFromTokens( tokens ) {
var checkContext, matcher, j,
len = tokens.length,
leadingRelative = Expr.relative[ tokens[0].type ],
implicitRelative = leadingRelative || Expr.relative[" "],
i = leadingRelative ? 1 : 0, // The foundational matcher ensures that elements are reachable from top-level context(s)
matchContext = addCombinator( function( elem ) {
return elem === checkContext;
}, implicitRelative, true ),
matchAnyContext = addCombinator( function( elem ) {
return indexOf.call( checkContext, elem ) > -1;
}, implicitRelative, true ),
matchers = [ function( elem, context, xml ) {
return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
(checkContext = context).nodeType ?
matchContext( elem, context, xml ) :
matchAnyContext( elem, context, xml ) );
} ]; for ( ; i < len; i++ ) {
if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
} else {
matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); // Return special upon seeing a positional matcher
if ( matcher[ expando ] ) {
// Find the next relative operator (if any) for proper handling
j = ++i;
for ( ; j < len; j++ ) {
if ( Expr.relative[ tokens[j].type ] ) {
break;
}
}
return setMatcher(
i > 1 && elementMatcher( matchers ),
i > 1 && toSelector(
// If the preceding token was a descendant combinator, insert an implicit any-element `*`
tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
).replace( rtrim, "$1" ),
matcher,
i < j && matcherFromTokens( tokens.slice( i, j ) ),
j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
j < len && toSelector( tokens )
);
}
matchers.push( matcher );
}
} return elementMatcher( matchers );
} function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
// A counter to specify which element is currently being matched
var matcherCachedRuns = 0,
bySet = setMatchers.length > 0,
byElement = elementMatchers.length > 0,
superMatcher = function( seed, context, xml, results, expandContext ) {
var elem, j, matcher,
setMatched = [],
matchedCount = 0,
i = "0",
unmatched = seed && [],
outermost = expandContext != null,
contextBackup = outermostContext,
// We must always have either seed elements or context
elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ),
// Use integer dirruns iff this is the outermost matcher
dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1); if ( outermost ) {
outermostContext = context !== document && context;
cachedruns = matcherCachedRuns;
} // Add elements passing elementMatchers directly to results
// Keep `i` a string if there are no elements so `matchedCount` will be "00" below
for ( ; (elem = elems[i]) != null; i++ ) {
if ( byElement && elem ) {
j = 0;
while ( (matcher = elementMatchers[j++]) ) {
if ( matcher( elem, context, xml ) ) {
results.push( elem );
break;
}
}
if ( outermost ) {
dirruns = dirrunsUnique;
cachedruns = ++matcherCachedRuns;
}
} // Track unmatched elements for set filters
if ( bySet ) {
// They will have gone through all possible matchers
if ( (elem = !matcher && elem) ) {
matchedCount--;
} // Lengthen the array for every element, matched or not
if ( seed ) {
unmatched.push( elem );
}
}
} // Apply set filters to unmatched elements
matchedCount += i;
if ( bySet && i !== matchedCount ) {
j = 0;
while ( (matcher = setMatchers[j++]) ) {
matcher( unmatched, setMatched, context, xml );
} if ( seed ) {
// Reintegrate element matches to eliminate the need for sorting
if ( matchedCount > 0 ) {
while ( i-- ) {
if ( !(unmatched[i] || setMatched[i]) ) {
setMatched[i] = pop.call( results );
}
}
} // Discard index placeholder values to get only actual matches
setMatched = condense( setMatched );
} // Add matches to results
push.apply( results, setMatched ); // Seedless set matches succeeding multiple successful matchers stipulate sorting
if ( outermost && !seed && setMatched.length > 0 &&
( matchedCount + setMatchers.length ) > 1 ) { Sizzle.uniqueSort( results );
}
} // Override manipulation of globals by nested matchers
if ( outermost ) {
dirruns = dirrunsUnique;
outermostContext = contextBackup;
} return unmatched;
}; return bySet ?
markFunction( superMatcher ) :
superMatcher;
} compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) {
var i,
setMatchers = [],
elementMatchers = [],
cached = compilerCache[ selector + " " ]; if ( !cached ) {
// Generate a function of recursive functions that can be used to check each element
if ( !group ) {
group = tokenize( selector );
}
i = group.length;
while ( i-- ) {
cached = matcherFromTokens( group[i] );
if ( cached[ expando ] ) {
setMatchers.push( cached );
} else {
elementMatchers.push( cached );
}
} // Cache the compiled function
cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
}
return cached;
}; function multipleContexts( selector, contexts, results ) {
var i = 0,
len = contexts.length;
for ( ; i < len; i++ ) {
Sizzle( selector, contexts[i], results );
}
return results;
} function select( selector, context, results, seed ) {
var i, tokens, token, type, find,
match = tokenize( selector ); if ( !seed ) {
// Try to minimize operations if there is only one group
if ( match.length === 1 ) { // Take a shortcut and set the context if the root selector is an ID
tokens = match[0] = match[0].slice( 0 );
if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
support.getById && context.nodeType === 9 && documentIsHTML &&
Expr.relative[ tokens[1].type ] ) { context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
if ( !context ) {
return results;
}
selector = selector.slice( tokens.shift().value.length );
} // Fetch a seed set for right-to-left matching
i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
while ( i-- ) {
token = tokens[i]; // Abort if we hit a combinator
if ( Expr.relative[ (type = token.type) ] ) {
break;
}
if ( (find = Expr.find[ type ]) ) {
// Search, expanding context for leading sibling combinators
if ( (seed = find(
token.matches[0].replace( runescape, funescape ),
rsibling.test( tokens[0].type ) && context.parentNode || context
)) ) { // If seed is empty or no tokens remain, we can return early
tokens.splice( i, 1 );
selector = seed.length && toSelector( tokens );
if ( !selector ) {
push.apply( results, seed );
return results;
} break;
}
}
}
}
} // Compile and execute a filtering function
// Provide `match` to avoid retokenization if we modified the selector above
compile( selector, match )(
seed,
context,
!documentIsHTML,
results,
rsibling.test( selector )
);
return results;
} // One-time assignments // Sort stability
support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; // Support: Chrome<14
// Always assume duplicates if they aren't passed to the comparison function
support.detectDuplicates = hasDuplicate; // Initialize against the default document
setDocument(); // Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
// Detached nodes confoundingly follow *each other*
support.sortDetached = assert(function( div1 ) {
// Should return 1, but returns 4 (following)
return div1.compareDocumentPosition( document.createElement("div") ) & 1;
}); // Support: IE<8
// Prevent attribute/property "interpolation"
// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
if ( !assert(function( div ) {
div.innerHTML = "<a href='#'></a>";
return div.firstChild.getAttribute("href") === "#" ;
}) ) {
addHandle( "type|href|height|width", function( elem, name, isXML ) {
if ( !isXML ) {
return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
}
});
} // Support: IE<9
// Use defaultValue in place of getAttribute("value")
if ( !support.attributes || !assert(function( div ) {
div.innerHTML = "<input/>";
div.firstChild.setAttribute( "value", "" );
return div.firstChild.getAttribute( "value" ) === "";
}) ) {
addHandle( "value", function( elem, name, isXML ) {
if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
return elem.defaultValue;
}
});
} // Support: IE<9
// Use getAttributeNode to fetch booleans when getAttribute lies
if ( !assert(function( div ) {
return div.getAttribute("disabled") == null;
}) ) {
addHandle( booleans, function( elem, name, isXML ) {
var val;
if ( !isXML ) {
return (val = elem.getAttributeNode( name )) && val.specified ?
val.value :
elem[ name ] === true ? name.toLowerCase() : null;
}
});
} jQuery.find = Sizzle;
jQuery.expr = Sizzle.selectors;
jQuery.expr[":"] = jQuery.expr.pseudos;
jQuery.unique = Sizzle.uniqueSort;
jQuery.text = Sizzle.getText;
jQuery.isXMLDoc = Sizzle.isXML;
jQuery.contains = Sizzle.contains; })( window );
}
///////////////////////////////////Sizzle END////////////////////////
///////////////////////////////////Callbacks BEGIN////////////////////////
{
// optionsCache : { 'once memory' : { once : true , memory : true } }
var optionsCache = {}; // once memory,options.match( core_rnotwhite )=[once, memory],function( _, flag )={once:true,memory:true}
function createOptions( options ) {
var object = optionsCache[ options ] = {};//中括号就是json的点,没有点了。
jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {
object[ flag ] = true;
});
return object;
} /*
var cb = $.Callbacks();
function aaa(){
alert(1);
}
cb.add(aaa);
(function(){
function bbb(){
alert(2);
}
cb.add(bbb);
})();
cb.fire();//1 2
*/
jQuery.Callbacks = function( options ) {//类的静态方法
//options 可选: once memory unique stopOnFalse
//方法:add remove has empty disable disabled lock locked fireWith fire fired
//什么都不写走jQuery.extend( {}, options ),返回空{},不这样写,如果options===undefined,后面还要做兼容。
options = typeof options === "string" ?
( optionsCache[ options ] || createOptions( options ) ) :
jQuery.extend( {}, options );//options={once:true,memory:true} var
memory,
fired,
firing,
firingStart,
firingLength,
firingIndex,
// 添加的所有方法
list = [],
stack = !options.once && [],
fire = function( data ) {
memory = options.memory && data;//有memory返回true
fired = true;//触发开始
firingIndex = firingStart || 0;
firingStart = 0;
firingLength = list.length;
firing = true;
for ( ; list && firingIndex < firingLength; firingIndex++ ) {
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {// 函数返回false,有stopOnFalse属性就不向下执行
memory = false; //memory也要置为false
break;
}
}
firing = false;//触发结束
if ( list ) {
if ( stack ) {
if ( stack.length ) {
fire( stack.shift() );
}
} else if ( memory ) {
list = [];
} else {
self.disable();
}
}
},
self = {//对外部接口
add: function() {
if ( list ) {//list一上来是空数组,会返回真,
var start = list.length;
(function add( args ) {//args是形参
jQuery.each( args, function( _, arg ) {//遍历传进来的多个方法名,
var type = jQuery.type( arg );
if ( type === "function" ) {
if ( !options.unique || !self.has( arg ) ) {//不是unioqye,或者是unipue但是没有
list.push( arg );
}
} else if ( arg && arg.length && type !== "string" ) {//数组,嵌套
add( arg );
}
});
})( arguments );//arguments是实参,方法名aaa,
if ( firing ) {
firingLength = list.length;
} else if ( memory ) {//第一次没有赋值是undefined,
firingStart = start;
fire( memory );
}
}
return this;
},
remove: function() {
if ( list ) {
jQuery.each( arguments, function( _, arg ) {//可以删除多个
var index;
while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
list.splice( index, 1 );//删除
if ( firing ) {
if ( index <= firingLength ) {
firingLength--;
}
if ( index <= firingIndex ) {
firingIndex--;
}
}
}
});
}
return this;
},
has: function( fn ) {
return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
},
empty: function() {
list = [];
firingLength = 0;
return this;
},
disable: function() {
list = stack = memory = undefined;
return this;
},
disabled: function() {
return !list;
},
lock: function() {
stack = undefined;
if ( !memory ) {
self.disable();
}
return this;
},
locked: function() {
return !stack;
},
fireWith: function( context, args ) {
if ( list && ( !fired || stack ) ) {
args = args || [];
args = [ context, args.slice ? args.slice() : args ];
if ( firing ) {
stack.push( args );
} else {
fire( args );
}
}
return this;
},
fire: function() {
self.fireWith( this, arguments );//cb.fire('hello');
return this;
},
fired: function() {
return !!fired;
}
}; return self;
};
}
///////////////////////////////////Callbacks END////////////////////////
///////////////////////////////////Deferred : 延迟对象 BEGIN////////////////////////
{
//延迟对象
jQuery.extend({ Deferred: function( func ) {
var tuples = [//resolve完成、reject未完成、notify进行中类似于fire,done、fail、progress回调函数的方法类似于add,jQuery.Callbacks()创建回调对象实现回调方法。映射数组。
// action, add listener, listener list, final state
[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
[ "notify", "progress", jQuery.Callbacks("memory") ]
],
state = "pending",
promise = {
state: function() {
return state;
},
always: function() {
deferred.done( arguments ).fail( arguments );
return this;
},
then: function( /* fnDone, fnFail, fnProgress */ ) {
var fns = arguments;
return jQuery.Deferred(function( newDefer ) {
jQuery.each( tuples, function( i, tuple ) {
var action = tuple[ 0 ],//得到状态
fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];//是函数得到这个函数,不是函数返回假,
// deferred[ done | fail | progress ] for forwarding actions to newDefer
deferred[ tuple[1] ](function() {
var returned = fn && fn.apply( this, arguments );
if ( returned && jQuery.isFunction( returned.promise ) ) {
returned.promise()
.done( newDefer.resolve )
.fail( newDefer.reject )
.progress( newDefer.notify );
} else {
newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
}
});
});
fns = null;
}).promise();
},
//jQuery.extend( obj, promise )这个promise是根据函数闭包,向上去找的,把promise复制给obj,
promise: function( obj ) {
return obj != null ? jQuery.extend( obj, promise ) : promise;
}
},
deferred = {}; // Keep pipe for back-compat
promise.pipe = promise.then; // 映射数组进行映射的函数
jQuery.each( tuples, function( i, tuple ) {
var list = tuple[ 2 ],//jQuery.Callbacks("once memory")
stateString = tuple[ 3 ];//resolved // promise[ done | fail | progress ] = list.add=jQuery.Callbacks("once memory").add方法
promise[ tuple[1] ] = list.add; // Handle state
if ( stateString ) {
list.add(function() {
// state = [ resolved | rejected ]
state = stateString; // [ reject_list | resolve_list ].disable; progress_list.lock
}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
} // deferred[ resolve | reject | notify ]
deferred[ tuple[0] ] = function() {//给deferred这个json赋值
deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
return this;
};
// deferred[ resolveWith | rejectWith | notifyWith ]=jQuery.Callbacks("once memory").fireWith方法
deferred[ tuple[0] + "With" ] = list.fireWith;//继续给deferred这个json赋值
}); // 把promise复制给deferred,deferred比promise多了3个方法,
promise.promise( deferred ); // Call given func if any
if ( func ) {
func.call( deferred, deferred );
} // All done!
return deferred;
}, // Deferred helper
when: function( subordinate /* , ..., subordinateN */ ) {
var i = 0,
resolveValues = core_slice.call( arguments ),
length = resolveValues.length, // 未完成函数的计数器,jQuery.isFunction( subordinate.promise ):subordinate的返回值是不是函数。
remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
deferred = remaining === 1 ? subordinate : jQuery.Deferred(), // Update function for both resolve and progress values
updateFunc = function( i, contexts, values ) {
return function( value ) {
contexts[ i ] = this;
values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;
if( values === progressValues ) {
deferred.notifyWith( contexts, values );
} else if ( !( --remaining ) ) {//减到0时触发resolveWith方法,
deferred.resolveWith( contexts, values );
}
};
}, progressValues, progressContexts, resolveContexts; // add listeners to Deferred subordinates; treat others as resolved
if ( length > 1 ) {
progressValues = new Array( length );
progressContexts = new Array( length );
resolveContexts = new Array( length );
for ( ; i < length; i++ ) {
if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
resolveValues[ i ].promise()//返回值
.done( updateFunc( i, resolveContexts, resolveValues ) )//触发完成函数
.fail( deferred.reject )//触发失败函数
.progress( updateFunc( i, progressContexts, progressValues ) );
} else {
--remaining;
}
}
} //计时器为0时执行resolveWith方法,
if ( !remaining ) {
deferred.resolveWith( resolveContexts, resolveValues );
} return deferred.promise();//执行done方法,
}
});
}
///////////////////////////////////Deferred : 延迟对象 END////////////////////////
///////////////////////////////////support : 功能检测 BEGIN////////////////////////
{
//兼容性,support是做兼容性检测,检测完之后实施是通过钩子机制。
jQuery.support = (function( support ) {//右边函数会立即执行
var input = document.createElement("input"),
fragment = document.createDocumentFragment(),//文档碎片
div = document.createElement("div"),
select = document.createElement("select"),
opt = select.appendChild( document.createElement("option") ); // input.type默认是text,基本都有,
if ( !input.type ) {
return support;
}
//改成复选框
input.type = "checkbox"; //新版本value的值是'on',老版本value的值是'',所以新本版support.checkOn=true,老版本support.checkOn=false,就可以通过support.checkOn属性值进行浏览器类型判断。
support.checkOn = input.value !== ""; // 狐火chrome下拉子项默认是选中的,ie默认不选中。
support.optSelected = opt.selected; // 定义初始值
support.reliableMarginRight = true;
support.boxSizingReliable = true;
support.pixelPosition = false; // Make sure checked status is properly cloned
// Support: IE9, IE10
//先让input选中,然后克隆,再判断选中状态,火狐chromeIE10以上都是选中的,ie低版本不是选中。
input.checked = true;
support.noCloneChecked = input.cloneNode( true ).checked; // Make sure that the options inside disabled selects aren't marked as disabled
// (WebKit marks them as disabled)
//下拉菜单禁止,子项有没有禁止。
select.disabled = true;
support.optDisabled = !opt.disabled; // Check if an input maintains its value after becoming a radio
// Support: IE9, IE10
input = document.createElement("input");
input.value = "t";
input.type = "radio";
support.radioValue = input.value === "t"; // #11217 - WebKit loses check when the name is after the checked attribute
input.setAttribute( "checked", "t" );
input.setAttribute( "name", "t" ); fragment.appendChild( input ); // Support: Safari 5.1, Android 4.x, Android 2.3
// old WebKit doesn't clone checked state correctly in fragments
support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; // Support: Firefox, Chrome, Safari
// Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP)
support.focusinBubbles = "onfocusin" in window; //首先定义一个div的背景剪切是内容剪切,然后复制一份设置背景剪切为空,然后看原来div的背景剪切还是不是内容剪切。
div.style.backgroundClip = "content-box";
div.cloneNode( true ).style.backgroundClip = "";
support.clearCloneStyle = div.style.backgroundClip === "content-box"; //下面的需要等dom加载完才能检测
jQuery(function() {
var container, marginDiv,
// Support: Firefox, Android 2.3 (Prefixed box-sizing versions).
//
divReset = "padding:0;margin:0;border:0;display:block;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box",
body = document.getElementsByTagName("body")[ 0 ]; if ( !body ) {
// Return for frameset docs that don't have a body
return;
}
//创建一个container来检测
container = document.createElement("div");
container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px"; // Check box-sizing and margin behavior.
body.appendChild( container ).appendChild( div );
div.innerHTML = "";
// Support: Firefox, Android 2.3 (Prefixed box-sizing versions).
div.style.cssText = "-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%"; // Workaround failing boxSizing test due to offsetWidth returning wrong value
// with some non-1 values of body zoom, ticket #13543
jQuery.swap( body, body.style.zoom != null ? { zoom: 1 } : {}, function() {
support.boxSizing = div.offsetWidth === 4;
}); // Use window.getComputedStyle because jsdom on node.js will break without it.
if ( window.getComputedStyle ) {
support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%";
support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px"; // Support: Android 2.3
// Check if div with explicit width and no margin-right incorrectly
// gets computed margin-right based on width of container. (#3333)
// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
marginDiv = div.appendChild( document.createElement("div") );
marginDiv.style.cssText = div.style.cssText = divReset;
marginDiv.style.marginRight = marginDiv.style.width = "0";
div.style.width = "1px"; support.reliableMarginRight =
!parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight );
} body.removeChild( container );
}); return support;//返回json
})( {} );//实参json
}
///////////////////////////////////support : 功能检测 END///////////////////////////
///////////////queue() : 队列方法 : 执行顺序的管理,data() : 数据缓存 begin//////////
{
/*
Implementation Summary 1. Enforce API surface and semantic compatibility with 1.9.x branch
2. Improve the module's maintainability by reducing the storage
paths to a single mechanism.
3. Use the same single mechanism to support "private" and "user" data.
4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
5. Avoid exposing implementation details on user objects (eg. expando properties)
6. Provide a clear path for implementation upgrade to WeakMap in 2014
*/
var data_user, data_priv,
rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
rmultiDash = /([A-Z])/g; function Data() {
// cache = {0:{} },并且这个0属性是不能被修改的。这个0是公用的。
Object.defineProperty( this.cache = {}, 0, {
get: function() {
return {};
}
});
//唯一标识,id
this.expando = jQuery.expando + Math.random();
}
/*
var cache = {
1 : { //同一个元素只有一个id
age : 30,
job : 'it',
'allName' : '妙味课堂'
},
2 : {
age : obj
}
*/
Data.uid = 1;//cache中可以累加的属性1,2,3 Data.accepts = function( owner ) {
// 1是元素,2是document,都是可以在cache中分配id的。
return owner.nodeType ?
owner.nodeType === 1 || owner.nodeType === 9 : true;
}; Data.prototype = {
//分配映射id或者0:空json
key: function( owner ) {//owner是document.body或者$('#div1')对象,
// 不是元素也不是document,就返回0,id就是0,在cache中返回空对象{},defineProperty中给cache定义的空对象。
if ( !Data.accepts( owner ) ) {
return 0;
}
var descriptor = {},
// 获取$('#div1')的属性this.expando的值,
unlock = owner[ this.expando ];
if ( !unlock ) {
unlock = Data.uid++;
try {
/*
descriptor={jQuery203089541586732714850.8840931279098725:{value:1}}
*/
descriptor[ this.expando ] = { value: unlock };
//给owner增加一个属性descriptor:json并且不能改变
Object.defineProperties( owner, descriptor ); // Support: Android < 4
// Fallback to a less secure definition
} catch ( e ) {
//兼容性:通过extend给owner增加{key:value}的键值对,这个可以被修改descriptor={jQuery203089541586732714850.8840931279098725:1}
descriptor[ this.expando ] = unlock;
jQuery.extend( owner, descriptor );
}
}
if ( !this.cache[ unlock ] ) {
this.cache[ unlock ] = {};//cache={1:{}}
}
return unlock;
},
//设置cache的值
set: function( owner, data, value ) {
var prop,
//找到这个元素的id和cache中的json
unlock = this.key( owner ),
cache = this.cache[ unlock ];//cache是某个id的json if ( typeof data === "string" ) {
cache[ data ] = value;//是字符串,就把key value加进去
//是json,$.data(document.body ,{ 'age' : 30 , 'job' : 'it' , 'allName' : '妙味课堂'});
} else {
// Fresh assignments by object are shallow copied
if ( jQuery.isEmptyObject( cache ) ) {
jQuery.extend( this.cache[ unlock ], data );//继承json过去
// Otherwise, copy the properties one-by-one to the cache object
} else {
for ( prop in data ) {
cache[ prop ] = data[ prop ];
}
}
}
return cache;
},
//获取cache中的值
get: function( owner, key ) {
var cache = this.cache[ this.key( owner ) ]; return key === undefined ?
cache : cache[ key ];
},
//set,get合体
//queue = data_priv.access( elem, type,jQuery.makeArray(data) );
access: function( owner, key, value ) {
var stored;
// In cases where either:
//
// 1. No key was specified
// 2. A string key was specified, but no value provided
//
// Take the "read" path and allow the get method to determine
// which value to return, respectively either:
//
// 1. The entire cache object
// 2. The data stored at the key
//
if ( key === undefined ||
((key && typeof key === "string") && value === undefined) ) {
//没有key就全部取出来,有key没有value只取
stored = this.get( owner, key ); return stored !== undefined ?
stored : this.get( owner, jQuery.camelCase(key) );
}
//有key有value,全部覆盖或者追加
// [*]When the key is not a string, or both a key and value
// are specified, set or extend (existing objects) with either:
//
// 1. An object of properties
// 2. A key and value
//
this.set( owner, key, value ); // Since the "set" path can have two possible entry points
// return the expected data based on which path was taken[*]
return value !== undefined ? value : key;
}, //移除cache
//data_priv.remove( elem, [ type + "queue", type + "queueHooks"; ] );
remove: function( owner, key ) {
var i, name, camel,
unlock = this.key( owner ),
cache = this.cache[ unlock ];//cache是某一个key的cache, if ( key === undefined ) {//不指定key,这个元素所有的都清空。
this.cache[ unlock ] = {}; } else {
// $.removeData(document.body , ['age','job','all-name']);
if ( jQuery.isArray( key ) ) {
//all-name找到allName
name = key.concat( key.map( jQuery.camelCase ) );
} else {
camel = jQuery.camelCase( key );
// Try the string as a key before any manipulation
if ( key in cache ) {
name = [ key, camel ];
} else {
// 转驼峰,去空格后在不在
name = camel;
name = name in cache ?
[ name ] : ( name.match( core_rnotwhite ) || [] );
}
} i = name.length;
while ( i-- ) {
delete cache[ name[ i ] ];
}
}
},
hasData: function( owner ) {
return !jQuery.isEmptyObject(
this.cache[ owner[ this.expando ] ] || {}
);
},
discard: function( owner ) {//删除1,2,这个整体
if ( owner[ this.expando ] ) {
delete this.cache[ owner[ this.expando ] ];
}
}
}; // These may be used throughout the jQuery core codebase
data_user = new Data();//cache就是依附在这个对象上
data_priv = new Data(); //对外提供的接口,通过$.直接调用。这个是方法的定义,准备给jQuery类调用的,里面的实现都是调用Data对象的实现。
jQuery.extend({
acceptData: Data.accepts, hasData: function( elem ) {
return data_user.hasData( elem ) || data_priv.hasData( elem );
}, data: function( elem, name, data ) {
return data_user.access( elem, name, data );
}, removeData: function( elem, name ) {
data_user.remove( elem, name );
}, // TODO: Now that all calls to _data and _removeData have been replaced
// with direct calls to data_priv methods, these can be deprecated.
//私有的,内部使用,不对外使用
_data: function( elem, name, data ) {
return data_priv.access( elem, name, data );
}, _removeData: function( elem, name ) {
data_priv.remove( elem, name );
}
}); //对外提供的接口,通过jQuery对象调用。设置一组元素时候是设置所有元素,获取元素只是获取第一个。
jQuery.fn.extend({
data: function( key, value ) {
var attrs, name,
elem = this[ 0 ],//如果是一组div,就只要第一个,
i = 0,
data = null; // Gets all values
if ( key === undefined ) {
if ( this.length ) {//$('#div1')找不到得到
data = data_user.get( elem );
//alert($('#div1').get(0).dataset.miaovAll);//h5特性,data-miaov-all="妙味"
//对h5特性的处理
if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) {
attrs = elem.attributes;//元素所有属性数组集合[title='123',class='box',data-miaov-all='苗圩',id='div1']
for ( ; i < attrs.length; i++ ) {
name = attrs[ i ].name;//属性的名字 if ( name.indexOf( "data-" ) === 0 ) {
//miaov-all转成miaoAll,js中不要出现横杆
name = jQuery.camelCase( name.slice(5) );
//存入cache
dataAttr( elem, name, data[ name ] );
}
}
data_priv.set( elem, "hasDataAttrs", true );
}
} return data;
} // Sets multiple values $('#div1').data({name:'hello',age:'30'});
if ( typeof key === "object" ) {
return this.each(function() {
data_user.set( this, key );
});
} return jQuery.access( this, function( value ) {
var data,
camelKey = jQuery.camelCase( key ); // The calling jQuery object (element matches) is not empty
// (and therefore has an element appears at this[ 0 ]) and the
// `value` parameter was not undefined. An empty jQuery object
// will result in `undefined` for elem = this[ 0 ] which will
// throw an exception if an attempt to read a data cache is made.
if ( elem && value === undefined ) {
// Attempt to get data from the cache
// with the key as-is
data = data_user.get( elem, key );
if ( data !== undefined ) {
return data;
} // Attempt to get data from the cache
// with the key camelized
data = data_user.get( elem, camelKey );
if ( data !== undefined ) {
return data;
} // Attempt to "discover" the data in
// HTML5 custom data-* attrs
data = dataAttr( elem, camelKey, undefined );
if ( data !== undefined ) {
return data;
} // We tried really hard, but the data doesn't exist.
return;
} // Set the data...
this.each(function() {
// First, attempt to store a copy or reference of any
// data that might've been store with a camelCased key.
var data = data_user.get( this, camelKey ); // For HTML5 data-* attribute interop, we have to
// store property names with dashes in a camelCase form.
// This might not apply to all properties...*
data_user.set( this, camelKey, value ); // *... In the case of properties that might _actually_
// have dashes, we need to also store a copy of that
// unchanged property.
if ( key.indexOf("-") !== -1 && data !== undefined ) {
data_user.set( this, key, value );
}
});
}, null, value, arguments.length > 1, null, true );
}, removeData: function( key ) {
return this.each(function() {
data_user.remove( this, key );
});
}
}); function dataAttr( elem, key, data ) {
var name; // If nothing was found internally, try to fetch any
// data from the HTML5 data-* attribute
if ( data === undefined && elem.nodeType === 1 ) {
name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
data = elem.getAttribute( name ); if ( typeof data === "string" ) {
try {
data = data === "true" ? true :
data === "false" ? false :
data === "null" ? null :
// Only convert to a number if it doesn't change the string
+data + "" === data ? +data :
rbrace.test( data ) ? JSON.parse( data ) :
data;
} catch( e ) {} // Make sure we set the data so it isn't changed later
data_user.set( elem, key, data );
} else {
data = undefined;
}
}
return data;
} //对外接口
jQuery.extend({
queue: function( elem, type, data ) {//入队。元素、队列名字、存进去的函数
//jQuery.queue( this, type, function( next, hooks ) {})
var queue; if ( elem ) {
type = ( type || "fx" ) + "queue";//不写队列名字就是fx
// $.queue( document , 'q1' , aaa||[aaa,bbb] );
queue = data_priv.get( elem, type );//get方法var cache = this.cache[ this.key( elem ) ];return type === undefined ? cache : cache[ type ];key方法return unlock = elem[ this.expando ];根据元素的唯一标识expando获取在data_priv对象中缓存中的1,2,3,4这个unlock数字,根据unlock这个数字获取cache中的json,根据type获取json中的值
/*
data_priv = {
1(document.203089541586732714850=1):{
name(type):'哈哈'(queue),
q1queue(type):...(queue数组),
}
}
*/ // Speed up dequeue by getting out quickly if this is just a lookup
if ( data ) {//函数名
if ( !queue || jQuery.isArray( data ) ) {//queue没有(函数是不是数组)或者queue有但是函数是数组,注意是数组就会把之前的全部覆盖。
/*
$.queue( document , 'q1' , aaa );
$.queue( document , 'q1' , bbb );
$.queue( document , 'q1' , [ccc] );
*/
queue = data_priv.access( elem, type, jQuery.makeArray(data) );
} else {//queue有(说明q1队列之前加过)并且函数不是数组直接push,注意queue没有的时候queue始终存的是一个数组
queue.push( data );
}
}
return queue || [];
}
}, dequeue: function( elem, type ) {
type = type || "fx";
//只能写jQuery.queue(),不能写queue(),queue()是jQuery类的静态方法,只能通过静态方式调用。
/*
jQuery.extend( {a:function(){alert(1)},b:function(){jQuery.a();}} )
$.b();
*/
var queue = jQuery.queue( elem, type ),//得到type对应的值,是一个数组,
startLength = queue.length,
fn = queue.shift(),//去除数组第一个
hooks = jQuery._queueHooks( elem, type ),
next = function() {
jQuery.dequeue( elem, type );//下一个,不能直接写dequeue,这行语句还没执行完时dequeue是不存在的
}; // If the fx queue is dequeued, always remove the progress sentinel
if ( fn === "inprogress" ) {
fn = queue.shift();
startLength--;
} if ( fn ) { // Add a progress sentinel to prevent the fx queue from being
// automatically dequeued
if ( type === "fx" ) {
queue.unshift( "inprogress" );//inprogress添进去
} // clear up the last queue stop function
delete hooks.stop;
fn.call( elem, next, hooks );//出队的函数执行,
} if ( !startLength && hooks ) {
hooks.empty.fire();
}
}, /*
data_priv = {
1(elem:document.203089541586732714850=1):{
name(type):'哈哈'(queue),
q1queue(type):...(queue数组),
}
}
*/
_queueHooks: function( elem, type ) {
var key = type + "queueHooks";
//有就获取,没有key,value都存在value是json就追加
return data_priv.get( elem, key ) || data_priv.access( elem, key, {
empty: jQuery.Callbacks("once memory").add(function() {
data_priv.remove( elem, [ type + "queue", key ] );
})
});
}
}); jQuery.fn.extend({
//this.queue( type, function( next, hooks ) {
//$('#div').queue('q1',bbb);
queue: function( type, data ) {
var setter = 2;
//$('#div').queue(bbb);
if ( typeof type !== "string" ) {//只传了一个函数,没有type
data = type;
type = "fx";
setter--;
}
///console.log( $('#div').queue('q1') ); 查看
if ( arguments.length < setter ) {
return jQuery.queue( this[0], type );//静态方法调用
} return data === undefined ?
this :
this.each(function() {//$('#div')是一个数组,对每一个设置
var queue = jQuery.queue( this, type, data );//每一个入队 // ensure a hooks for this queue
jQuery._queueHooks( this, type ); if ( type === "fx" && queue[0] !== "inprogress" ) {
jQuery.dequeue( this, type );//第一个入队然后立即出队,调用
}
});
},
dequeue: function( type ) {
return this.each(function() {
jQuery.dequeue( this, type );//每一个出队
});
}, //.delay(2000)
/*
jQuery.fx.speeds = {
slow: 600,
fast: 200,
// Default speed
_default: 400
};
*/
//jQuery.fx = Tween.prototype.init;
delay: function( time, type ) {
time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
type = type || "fx";
//queue第二个参数是匿名函数,也添加进去
return this.queue( type, function( next, hooks ) {
var timeout = setTimeout( next, time );
hooks.stop = function() {
clearTimeout( timeout );
};
});
},
clearQueue: function( type ) {
return this.queue( type || "fx", [] );//空数组是真
},
//队列全部执行完之后,再去调用
promise: function( type, obj ) {
var tmp,
count = 1,
defer = jQuery.Deferred(),
elements = this,
i = this.length,
resolve = function() {
if ( !( --count ) ) {
defer.resolveWith( elements, [ elements ] );
}
}; if ( typeof type !== "string" ) {
obj = type;
type = undefined;
}
type = type || "fx"; while( i-- ) {
tmp = data_priv.get( elements[ i ], type + "queueHooks" );
if ( tmp && tmp.empty ) {
count++;
tmp.empty.add( resolve );
}
}
resolve();
return defer.promise( obj );
}
});
}
//////////////////queue() : 队列方法 : 执行顺序的管理,data() : 数据缓存 END///////////////
////////////////attr() prop() val() addClass()等 : 对元素属性的操作BEGIN////////////
{
var nodeHook, boolHook,
rclass = /[\t\r\n\f]/g,
rreturn = /\r/g,
rfocusable = /^(?:input|select|textarea|button)$/i; jQuery.fn.extend({
attr: function( name, value ) {
//遍历this
//arguments.length > 1,jQuery.attr(this[i],name,value),返回this
//arguments.length <= 1,jQuery.attr(this[i],name,value),返回this
return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
}, removeAttr: function( name ) {
return this.each(function() {
jQuery.removeAttr( this, name );
});
}, prop: function( name, value ) {
return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
}, removeProp: function( name ) {
return this.each(function() {
delete this[ jQuery.propFix[ name ] || name ];
});
}, //$('.div1').addClass('box2 box3');
addClass: function( value ) {
var classes, elem, cur, clazz, j,
i = 0,
len = this.length,
proceed = typeof value === "string" && value;//是字符串返回字符串,不是返回flase
/*
$('div').addClass( function(index){
alert(index);
return 'box'+index;
});
*/
if ( jQuery.isFunction( value ) ) {
console.log(this);//这里的this是jQuery对象,Object { 0: <div#div1.box>, 1: <div#div2.box>, 2: <div#div3.box>, length: 3},通过return ( context || rootjQuery ).find('.div1')原生方法获得,里面每一个是节点对象不是jQuery对象
return this.each(function( j ) {
console.log(this);//这里不是jQuery对象是dom节点对象,<div id='div1'></div>,<div id='div2'></div>,<div id='div3'></div>
console.log(jQuery( this ));//jQuery( this )是jQuery对象,Object { 0: <div#div1.box>, context: <div#div1.box>, length: 1 },Object { 0: <div#div2.box>, context: <div#div2.box>, length: 1 },Object { 0: <div#div3.box>, context: <div#div3.box>, length: 1 }
/*jQuery( this )走的是
if ( selector.nodeType ) {//节点都有nodeType属性
this.context = this[0] = selector;
this.length = 1;
return this;*/
jQuery( this ).addClass( value.call( this, j, this.className ) );
});
}
if ( proceed ) {
// 把字符串正则分割成数组
classes = ( value || "" ).match( core_rnotwhite ) || [];
for ( ; i < len; i++ ) {
elem = this[ i ];
//不是元素节点返回false,elem.className元素有没有class属性,有就合并(重复不合并),cur是之前的class
cur = elem.nodeType === 1 && ( elem.className ?
//非空格转换成空格
( " " + elem.className + " " ).replace( rclass, " " ) :
" "
);
if ( cur ) {
j = 0;
while ( (clazz = classes[j++]) ) {
if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
cur += clazz + " ";
}
}//前后去空格
elem.className = jQuery.trim( cur );
}
}
}
return this;
}, removeClass: function( value ) {
var classes, elem, cur, clazz, j,
i = 0,
len = this.length,
//先执行&&再||,proceed为true参数长度是0删除所有或者参数是字符串,为false传的不是字符串
proceed = arguments.length === 0 || typeof value === "string" && value;
if ( jQuery.isFunction( value ) ) {
return this.each(function( j ) {
jQuery( this ).removeClass( value.call( this, j, this.className ) );
});
}
if ( proceed ) {
classes = ( value || "" ).match( core_rnotwhite ) || [];
for ( ; i < len; i++ ) {
elem = this[ i ];
// This expression is here for better compressibility (see addClass)
cur = elem.nodeType === 1 && ( elem.className ?
( " " + elem.className + " " ).replace( rclass, " " ) :
""
);
if ( cur ) {
j = 0;
while ( (clazz = classes[j++]) ) {
// Remove *all* instances
while ( cur.indexOf( " " + clazz + " " ) >= 0 ) {
cur = cur.replace( " " + clazz + " ", " " );
}
}
elem.className = value ? jQuery.trim( cur ) : "";
}
}
}
return this;
}, toggleClass: function( value, stateVal ) {
var type = typeof value;
//$('#div1').toggleClass('box2 box3',true);//有没有都是add
//$('#div1').toggleClass('box2 box3',false);//有没有都是删除
if ( typeof stateVal === "boolean" && type === "string" ) {//真就添加,假就删除
return stateVal ? this.addClass( value ) : this.removeClass( value );
} if ( jQuery.isFunction( value ) ) {
return this.each(function( i ) {
jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
});
}
//$('#div1').toggleClass('box2 box3')
return this.each(function() {
if ( type === "string" ) {
// toggle individual class names
var className,
i = 0,
self = jQuery( this ),//转成jQuery对象,hasClass是jQuery对象的方法。
//空格分割成数组
classNames = value.match( core_rnotwhite ) || []; while ( (className = classNames[ i++ ]) ) {
// check each className given, space separated list
if ( self.hasClass( className ) ) {
self.removeClass( className );
} else {
self.addClass( className );
}
} // Toggle whole class name
//$('#div1').toggleClass(false);
} else if ( type === core_strundefined || type === "boolean" ) {
if ( this.className ) {
// store className if set
data_priv.set( this, "__className__", this.className );
} // If the element has a class name or if we're passed "false",
// then remove the whole classname (if there was one, the above saved it).
// Otherwise bring back whatever was previously saved (if anything),
// falling back to the empty string if nothing was stored.
this.className = this.className || value === false ? "" : data_priv.get( this, "__className__" ) || "";
}
});
}, hasClass: function( selector ) {
var className = " " + selector + " ",
i = 0,
l = this.length;
for ( ; i < l; i++ ) {
if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) {
return true;
}
} return false;
}, val: function( value ) {
var hooks, ret, isFunction,
elem = this[0];
//$('#input1').val()
if ( !arguments.length ) {//获取
if ( elem ) {//只获取第一个元素
//hooks兼容处理,jQuery.valHooks[ elem.type ]在valHooks 这个json中找不到就找jQuery.valHooks[ elem.nodeName.toLowerCase() ]
hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
/*
valHooks: {
hooks = option: { //elem.type || elem.nodeName.toLowerCase()
get: function( elem ) {}
},
hooks = select: {
get: function( elem ) {},
set: function( elem, value ) {}
}
下面的:
hooks = radio: {
set: function( elem ) {}
get: function( elem, value ) {}
},
hooks = checkbox: {
set: function( elem ) {},
get: function( elem, value ) {}
}
}
*/
if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
return ret;
}
//不再hooks里面
ret = elem.value; return typeof ret === "string" ?
// handle most common string cases
ret.replace(rreturn, "") :
// handle cases where value is null/undef or number
ret == null ? "" : ret;
} return;
}
//设置
isFunction = jQuery.isFunction( value ); return this.each(function( i ) {
var val;
if ( this.nodeType !== 1 ) {
return;
}
if ( isFunction ) {
val = value.call( this, i, jQuery( this ).val() );
} else {
val = value;
}
// Treat null/undefined as ""; convert numbers to string
if ( val == null ) {//$('#input1').val(null);
val = "";
} else if ( typeof val === "number" ) {//$('#input1').val(123123);
val += "";//转成字符串
} else if ( jQuery.isArray( val ) ) {//$('#input2').val(['hello']);
val = jQuery.map(val, function ( value ) {
return value == null ? "" : value + "";
});
}
hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
// If set returns undefined, fall back to normal setting
if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
this.value = val;
}
});
}
}); jQuery.extend({//静态属性只能通过jQuery静态方式调
valHooks: {//option-get,select-get.select-set 兼容性处理
option: {
get: function( elem ) {
// attributes.value is undefined in Blackberry 4.7 but
// uses .value. See #6932
var val = elem.attributes.value;
//val不存在输出elem.value,val存在specified为false走elem.text
return !val || val.specified ? elem.value : elem.text;
}
},
select: {
//$('select').val()
get: function( elem ) {
var value, option,
options = elem.options,//下拉选项
index = elem.selectedIndex,//当前索引值
//select只选了一个或者没有选,one为true,就是单选
one = elem.type === "select-one" || index < 0,
//one为true时单选values是空,one是false时多选values是一个数组存储所有的选择的多个
values = one ? null : [],
//单选时max是当前索引加1,多选时是下拉选项的长度
max = one ? index + 1 : options.length,
//
i = index < 0 ?
max /*index < 0没有选择时one=true,i=max=0*/
:one ?
index/*index >= 0有选择时,select-one单选one=true,i=index,max=index+1,*/
:0 /*index >= 0有选择时,不是select-one多选one=false,i=0,max=options.length*/
; // 没有选择不进入循环,不获取select的val()
//有选择单选,i=index,只获取index的val()
//有选择好多选,全部获取
for ( ; i < max; i++ ) {
option = options[ i ];//js对象 // IE6-9 doesn't update selected after form reset (#2551)
if ( ( option.selected || i === index ) &&
// Don't return options that are disabled or in a disabled optgroup
( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) &&
( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { // Get the specific value for the option
value = jQuery( option ).val();//转成jQuery对象 // We don't need an array for one selects
if ( one ) {
return value;
} // Multi-Selects return an array
values.push( value );
}
} return values;
},
// $('#select').val(111);//111被选中了
set: function( elem, value ) {
var optionSet, option,
options = elem.options,//所有的下拉选项,js对象
values = jQuery.makeArray( value ),//转成数组
i = options.length; while ( i-- ) {//遍历
option = options[ i ];
if ( (option.selected = jQuery.inArray( jQuery(option).val(), values ) >= 0) ) {//在数组里面就把她设为选中
optionSet = true;
}
} // force browsers to behave consistently when non-matching value is set
if ( !optionSet ) {//都没有
elem.selectedIndex = -1;
}
return values;
}
}
}, attr: function( elem, name, value ) {
var hooks, ret,
nType = elem.nodeType; // 节点不存在,或者文本、属性、注释节点
if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
return;
} // core_strundefined = typeof undefined,
if ( typeof elem.getAttribute === core_strundefined ) {
//$(document).attr('title','hello'); 走这里通过.设置
return jQuery.prop( elem, name, value );
} // 1是元素节点,
if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
name = name.toLowerCase();
//只有type才做兼容性处理
hooks = jQuery.attrHooks[ name ] ||
//$('input').attr('checked',true);//没问题,做兼容了
( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook );
} if ( value !== undefined ) {//设置
//$('#div1').attr('miaov',null); 调用remove
if ( value === null ) {
jQuery.removeAttr( elem, name );
//hooks中,set方法存在,就调用set方法并且返回值存在,就返回返回值
} else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
return ret;//有兼容性执行兼容操作,返回值 } else {//没有兼容性操作设置值
elem.setAttribute( name, value + "" );
return value;
}
//hooks中,get方法存在,就调用get方法并且返回值存在,就返回返回值
} else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {//获取,有兼容性返回值
return ret; } else {//获取没有兼容性时
ret = jQuery.find.attr( elem, name ); // Non-existent attributes return null, we normalize to undefined
return ret == null ?
undefined :
ret;
}
}, removeAttr: function( elem, value ) {
var name, propName,
i = 0,
//$('#div1').removeAttr('maio href id');
attrNames = value && value.match( core_rnotwhite );//core_rnotwhite = /\S+/g, 非空格,返回数组 if ( attrNames && elem.nodeType === 1 ) {
while ( (name = attrNames[i++]) ) {
/*
propFix: {
"for": "htmlFor",
"class": "className"
},
*/// $('#div1').removeAttr('class');
propName = jQuery.propFix[ name ] || name; // Boolean attributes get special treatment (#10870)
if ( jQuery.expr.match.bool.test( name ) ) {
// $('#div1').removeAttr('checked');
elem[ propName ] = false;
} elem.removeAttribute( name );//调用原生
}
}
},
//hooks = jQuery.attrHooks[ name ]
attrHooks: {
type: {//只有name = 'type',才会有有兼容性判断。
set: function( elem, value ) {//只有set说明兼容只是针对设置没有获取
if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {//单选值的兼容
// Setting the type on a radio button after the value resets the value in IE6-9
// Reset value to default in case type is set after value during creation //当设置type = 'radio'时IE会有兼容性问题,所以要先设置类型才设置值
var val = elem.value;
elem.setAttribute( "type", value );
if ( val ) {
elem.value = val;
}
return value;
}
}
}
}, propFix: {
"for": "htmlFor",
"class": "className"
}, prop: function( elem, name, value ) {
var ret, hooks, notxml,
nType = elem.nodeType; // don't get/set properties on text, comment and attribute nodes
if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
return;
} notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); if ( notxml ) {
// Fix name and attach hooks
name = jQuery.propFix[ name ] || name;
hooks = jQuery.propHooks[ name ];//兼容性处理
} if ( value !== undefined ) {//设置值
return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ?
ret :
( elem[ name ] = value );//prop使用的是.操作 } else {//获取值
return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ?
ret :
elem[ name ];
}
}, propHooks: {
tabIndex: {//光标切换顺序,只对tabIndex属性做兼容
get: function( elem ) {//只对get方法做兼容
return elem.hasAttribute( "tabindex" ) || rfocusable.test( elem.nodeName ) || elem.href ?
elem.tabIndex :
-1;
}
}
}
}); // Hooks for boolean attributes
boolHook = {
set: function( elem, value, name ) {
if ( value === false ) {
// Remove boolean attributes when set to false
jQuery.removeAttr( elem, name );
} else {
elem.setAttribute( name, name );
}
return name;
}
};
jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) {
var getter = jQuery.expr.attrHandle[ name ] || jQuery.find.attr; jQuery.expr.attrHandle[ name ] = function( elem, name, isXML ) {
var fn = jQuery.expr.attrHandle[ name ],
ret = isXML ?
undefined :
/* jshint eqeqeq: false */
// Temporarily disable this handler to check existence
(jQuery.expr.attrHandle[ name ] = undefined) !=
getter( elem, name, isXML ) ? name.toLowerCase() :
null; // Restore handler
jQuery.expr.attrHandle[ name ] = fn; return ret;
};
}); // Support: IE9+
// Selectedness for an option in an optgroup can be inaccurate
if ( !jQuery.support.optSelected ) {
jQuery.propHooks.selected = {
get: function( elem ) {
var parent = elem.parentNode;
if ( parent && parent.parentNode ) {
parent.parentNode.selectedIndex;
}
return null;
}
};
} jQuery.each([
"tabIndex",
"readOnly",
"maxLength",
"cellSpacing",
"cellPadding",
"rowSpan",
"colSpan",
"useMap",
"frameBorder",
"contentEditable"
], function() {
//value = callback.call( obj[ i ], i, obj[ i ] );
jQuery.propFix[ this.toLowerCase() ] = this;
}); /*
valHooks: {
hooks = radio: {
set: function( elem ) {}
get: function( elem, value ) {}
},
hooks = checkbox: {
set: function( elem ) {},
get: function( elem, value ) {}
}
}
*/
jQuery.each([ "radio", "checkbox" ], function() {
jQuery.valHooks[ this ] = {
//$('#radio').val(['hello']);
set: function( elem, value ) {
if ( jQuery.isArray( value ) ) {
return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );//设置选中状态
}
}
};
if ( !jQuery.support.checkOn ) {
//有的话做处理,没有不做处理 //获取单选框和复选框的value值时绝大多数浏览器返回的都是on,有些是空的,
jQuery.valHooks[ this ].get = function( elem ) {
// Support: Webkit
// "" is returned instead of "on" if a value isn't specified
return elem.getAttribute("value") === null ? "on" : elem.value;
};
}
}); }
//////////////////attr() prop() val() addClass()等 : 对元素属性的操作END////////////
//////////////////事件操作begin////////////////////////////////////
{
var rkeyEvent = /^key/,
rmouseEvent = /^(?:mouse|contextmenu)|click/,
rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; function returnTrue() {
return true;
} function returnFalse() {
return false;
} function safeActiveElement() {
try {
return document.activeElement;
} catch ( err ) { }
} /*
* Helper functions for managing events -- not part of the public interface.
* Props to Dean Edwards' addEvent library for many of the ideas.
*/
jQuery.event = { global: {},
//add( $('#div1'), 'click.bbb.aaa', fn, {name:'hello'}, 'li' )
add: function( elem, types, handler, data, selector ) { var handleObjIn, eventHandle, tmp,
events, t, handleObj,
special, handlers, type, namespaces, origType,
elemData = data_priv.get( elem );
if ( !elemData ) {
return;
} // Caller can pass in an object of custom data in lieu of the handler
if ( handler.handler ) {
handleObjIn = handler;
handler = handleObjIn.handler;
selector = handleObjIn.selector;
} // Make sure that the handler has a unique ID, used to find/remove it later
if ( !handler.guid ) {
handler.guid = jQuery.guid++;//函数有一个guid
} // Init the element's event structure and main handler, if this is the first
//span.listeners = span.listeners || {};
if ( !(events = elemData.events) ) {
events = elemData.events = {}; //不存在就创建
}
if ( !(eventHandle = elemData.handle) ) {
eventHandle = elemData.handle = function( e ) {
// Discard the second event of a jQuery.event.trigger() and
// when an event is called after a page has unloaded
return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ?
//发送事件
jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
undefined;
};
// Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
eventHandle.elem = elem;
} // $('#div1').on('click mouseover mousedown',function(a){})
types = ( types || "" ).match( core_rnotwhite ) || [""];
t = types.length;
while ( t-- ) {
// $('#div1').on('click.bbb.aaa',function(a){})
tmp = rtypenamespace.exec( types[t] ) || [];
type = origType = tmp[1];
//命名空间排序
namespaces = ( tmp[2] || "" ).split( "." ).sort(); // There *must* be a type, no attaching namespace-only handlers
if ( !type ) {
continue;
} //处理特殊事件
special = jQuery.event.special[ type ] || {}; // If selector defined, determine special event api type, otherwise given type
type = ( selector ? special.delegateType : special.bindType ) || type; // Update special based on newly reset type
special = jQuery.event.special[ type ] || {}; // handleObj is passed to all event handlers
handleObj = jQuery.extend({
type: type,
origType: origType,
data: data,
handler: handler,
guid: handler.guid,//guid是函数的guid
selector: selector,
needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
namespace: namespaces.join(".")
}, handleObjIn ); // Init the event handler queue if we're the first
//span.listeners[eventName] = span.listeners[eventName] || [];
if ( !(handlers = events[ type ]) ) {
handlers = events[ type ] = [];
handlers.delegateCount = 0;//委托计数 // Only use addEventListener if the special events handler returns false
//obj.addEventListener(eventName,fn,false);
if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
if ( elem.addEventListener ) {
//自带事件绑定,自定义事件走这里但是没有任何效果,
elem.addEventListener( type, eventHandle, false );
}
}
} if ( special.add ) {
special.add.call( elem, handleObj ); if ( !handleObj.handler.guid ) {
handleObj.handler.guid = handler.guid;
}
} // Add to the element's handler list, delegates in front
//obj.listeners[eventName].push(fn);
if ( selector ) {
handlers.splice( handlers.delegateCount++, 0, handleObj );
} else {
handlers.push( handleObj );
} // Keep track of which events have ever been used, for event optimization
jQuery.event.global[ type ] = true;
} // Nullify elem to prevent memory leaks in IE
elem = null;//防止内存泄漏,
}, // Detach an event or set of events from an element
remove: function( elem, types, handler, selector, mappedTypes ) { var j, origCount, tmp,
events, t, handleObj,
special, handlers, type, namespaces, origType,
elemData = data_priv.hasData( elem ) && data_priv.get( elem ); if ( !elemData || !(events = elemData.events) ) {
return;
} // Once for each type.namespace in types; type may be omitted
types = ( types || "" ).match( core_rnotwhite ) || [""];
t = types.length;
while ( t-- ) {
tmp = rtypenamespace.exec( types[t] ) || [];
type = origType = tmp[1];
namespaces = ( tmp[2] || "" ).split( "." ).sort(); // Unbind all events (on this namespace, if provided) for the element
if ( !type ) {
for ( type in events ) {
jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
}
continue;
} special = jQuery.event.special[ type ] || {};
type = ( selector ? special.delegateType : special.bindType ) || type;
handlers = events[ type ] || [];
tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); // Remove matching events
origCount = j = handlers.length;
while ( j-- ) {
handleObj = handlers[ j ]; if ( ( mappedTypes || origType === handleObj.origType ) &&
( !handler || handler.guid === handleObj.guid ) &&
( !tmp || tmp.test( handleObj.namespace ) ) &&
( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
handlers.splice( j, 1 ); if ( handleObj.selector ) {
handlers.delegateCount--;
}
if ( special.remove ) {
special.remove.call( elem, handleObj );
}
}
} // Remove generic event handler if we removed something and no more handlers exist
// (avoids potential for endless recursion during removal of special event handlers)
//obj.removeEventListener(eventName,fn,false);
if ( origCount && !handlers.length ) {
if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
jQuery.removeEvent( elem, type, elemData.handle );
}
//delete obj.listeners[eventName]; 针对自定义事件操作
delete events[ type ];
}
} // Remove the expando if it's no longer used
if ( jQuery.isEmptyObject( events ) ) {
delete elemData.handle;
data_priv.remove( elem, "events" );
}
}, trigger: function( event, data, elem, onlyHandlers ) { var i, cur, tmp, bubbleType, ontype, handle, special,
eventPath = [ elem || document ],
type = core_hasOwn.call( event, "type" ) ? event.type : event,
namespaces = core_hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; cur = tmp = elem = elem || document; // Don't do events on text and comment nodes
if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
return;
} // focus/blur morphs to focusin/out; ensure we're not firing them right now
if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
return;
} if ( type.indexOf(".") >= 0 ) {
// Namespaced trigger; create a regexp to match event type in handle()
namespaces = type.split(".");
type = namespaces.shift();
namespaces.sort();
}
ontype = type.indexOf(":") < 0 && "on" + type; // Caller can pass in a jQuery.Event object, Object, or just an event type string
event = event[ jQuery.expando ] ?
event :
new jQuery.Event( type, typeof event === "object" && event ); // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
event.isTrigger = onlyHandlers ? 2 : 3;
event.namespace = namespaces.join(".");
event.namespace_re = event.namespace ?
new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
null; // Clean up the event in case it is being reused
event.result = undefined;
if ( !event.target ) {
event.target = elem;
} // Clone any incoming data and prepend the event, creating the handler arg list
data = data == null ?
[ event ] :
jQuery.makeArray( data, [ event ] ); // Allow special events to draw outside the lines
special = jQuery.event.special[ type ] || {};
if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
return;
} // Determine event propagation path in advance, per W3C events spec (#9951)
// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { bubbleType = special.delegateType || type;
if ( !rfocusMorph.test( bubbleType + type ) ) {
cur = cur.parentNode;
}
for ( ; cur; cur = cur.parentNode ) {
eventPath.push( cur );
tmp = cur;
} // Only add window if we got to document (e.g., not plain obj or detached DOM)
if ( tmp === (elem.ownerDocument || document) ) {
eventPath.push( tmp.defaultView || tmp.parentWindow || window );
}
} // Fire handlers on the event path
//for(var i=0;i<arr.length;i++){ arr[i]();//函数执行 }
i = 0;
while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { event.type = i > 1 ?
bubbleType :
special.bindType || type; // jQuery handler
handle = ( data_priv.get( cur, "events" ) || {} )[ event.type ] && data_priv.get( cur, "handle" );
if ( handle ) {
handle.apply( cur, data );
} // Native handler
handle = ontype && cur[ ontype ];
if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) {
event.preventDefault();
}
}
event.type = type; // If nobody prevented the default action, do it now
if ( !onlyHandlers && !event.isDefaultPrevented() ) { if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) &&
jQuery.acceptData( elem ) ) { // Call a native DOM method on the target with the same name name as the event.
// Don't do default actions on window, that's where global variables be (#6170)
if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) { // Don't re-trigger an onFOO event when we call its FOO() method
tmp = elem[ ontype ]; if ( tmp ) {
elem[ ontype ] = null;
} // Prevent re-triggering of the same event, since we already bubbled it above
jQuery.event.triggered = type;
elem[ type ]();
jQuery.event.triggered = undefined; if ( tmp ) {
elem[ ontype ] = tmp;
}
}
}
} return event.result;
}, /*
eventHandle = elemData.handle = function( e ) {
jQuery.event.dispatch.apply( eventHandle.elem, arguments )
}
*/
//配发事件的具体操作
dispatch: function( event ) {//event是原生的event
//event兼容性处理
event = jQuery.event.fix( event );
var i, j, ret, matched, handleObj,
handlerQueue = [],
args = core_slice.call( arguments ),
handlers = ( data_priv.get( this, "events" ) || {} )[ event.type ] || [],
special = jQuery.event.special[ event.type ] || {}; // Use the fix-ed jQuery.Event rather than the (read-only) native event
args[0] = event;
event.delegateTarget = this; // Call the preDispatch hook for the mapped type, and let it bail if desired
if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
return;
} // 执行顺序的队列,
handlerQueue = jQuery.event.handlers.call( this, event, handlers ); // Run delegates first; they may want to stop propagation beneath us
i = 0;
while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {
event.currentTarget = matched.elem; j = 0;
while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { // Triggered event must either 1) have no namespace, or
// 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { event.handleObj = handleObj;
event.data = handleObj.data; ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
.apply( matched.elem, args ); if ( ret !== undefined ) {
if ( (event.result = ret) === false ) {//return false就是阻止冒泡和阻止默认事件
event.preventDefault();
event.stopPropagation();
}
}
}
}
} // Call the postDispatch hook for the mapped type
if ( special.postDispatch ) {
special.postDispatch.call( this, event );
} return event.result;
}, handlers: function( event, handlers ) {
var i, matches, sel, handleObj,
handlerQueue = [],
delegateCount = handlers.delegateCount,
cur = event.target; // Find delegate handlers
// Black-hole SVG <use> instance trees (#13180)
// Avoid non-left-click bubbling in Firefox (#3861)
if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { for ( ; cur !== this; cur = cur.parentNode || this ) { // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
if ( cur.disabled !== true || event.type !== "click" ) {
matches = [];
for ( i = 0; i < delegateCount; i++ ) {
handleObj = handlers[ i ]; // Don't conflict with Object.prototype properties (#13203)
sel = handleObj.selector + " "; if ( matches[ sel ] === undefined ) {
matches[ sel ] = handleObj.needsContext ?
jQuery( sel, this ).index( cur ) >= 0 :
jQuery.find( sel, this, null, [ cur ] ).length;
}
if ( matches[ sel ] ) {
matches.push( handleObj );
}
}
if ( matches.length ) {
handlerQueue.push({ elem: cur, handlers: matches });
}
}
}
} // Add the remaining (directly-bound) handlers
if ( delegateCount < handlers.length ) {
handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });
} return handlerQueue;
}, //JQ中共享原生JS的event属性
props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), fixHooks: {},
//键盘的event兼容
keyHooks: {
props: "char charCode key keyCode".split(" "),//公有的属性
filter: function( event, original ) {//兼容性处理
// 对which做的兼容性处理
if ( event.which == null ) {//which有没有
//看charCode有没有,再看keyCode有没有
event.which = original.charCode != null ? original.charCode : original.keyCode;
} return event;
}
},
//鼠标的event兼容
mouseHooks: {
props: "button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
filter: function( event, original ) {
var eventDoc, doc, body,
button = original.button; // Calculate pageX/Y if missing and clientX/Y available
if ( event.pageX == null && original.clientX != null ) {
eventDoc = event.target.ownerDocument || document;
doc = eventDoc.documentElement;
body = eventDoc.body; event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 );
} // Add which for click: 1 === left; 2 === middle; 3 === right
// Note: button is not normalized, so don't use it
if ( !event.which && button !== undefined ) {
event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
} return event;
}
},
//event对象的兼容处理
fix: function( event ) {
if ( event[ jQuery.expando ] ) {//缓存
return event;
} // Create a writable copy of the event object and normalize some properties
var i, prop, copy,
type = event.type,
originalEvent = event,
fixHook = this.fixHooks[ type ];//type=click if ( !fixHook ) {//兼容事件处理
this.fixHooks[ type ] = fixHook =
rmouseEvent.test( type ) ? this.mouseHooks :
rkeyEvent.test( type ) ? this.keyHooks :
{};
}
copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
//创建一个增强版的event,原生没有的event
event = new jQuery.Event( originalEvent ); i = copy.length;
while ( i-- ) {
prop = copy[ i ];
//原生的button buttons clientX clientY offsetX offsetY pageX 等 赋值给jQuery的event,
event[ prop ] = originalEvent[ prop ];
} // Support: Cordova 2.5 (WebKit) (#13255)
// All events should have a target; Cordova deviceready doesn't
if ( !event.target ) {
event.target = document;
} // Support: Safari 6.0+, Chrome < 28
// Target should not be a text node (#504, #13143)
if ( event.target.nodeType === 3 ) {
event.target = event.target.parentNode;
}
//得到兼容之后的event对象
return fixHook.filter? fixHook.filter( event, originalEvent ) : event;
}, special: {
load: {
noBubble: true //load事件不冒泡
},
focus: {
// Fire native event if possible so blur/focus sequence is correct
trigger: function() {//
if ( this !== safeActiveElement() && this.focus ) {
this.focus();
return false;
}
},
//委托给focusin事件
delegateType: "focusin"
},
blur: {
trigger: function() {
if ( this === safeActiveElement() && this.blur ) {
this.blur();
return false;
}
},
delegateType: "focusout"
},
click: {
// For checkbox, fire native event so checked state will be right
trigger: function() {
if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) {
this.click();
return false;
}
}, // For cross-browser consistency, don't fire native .click() on links
_default: function( event ) {
return jQuery.nodeName( event.target, "a" );
}
}, beforeunload: {
postDispatch: function( event ) { // Support: Firefox 20+
// Firefox doesn't alert if the returnValue field is not set.
if ( event.result !== undefined ) {
event.originalEvent.returnValue = event.result;
}
}
}
}, simulate: function( type, elem, event, bubble ) {
// Piggyback on a donor event to simulate a different one.
// Fake originalEvent to avoid donor's stopPropagation, but if the
// simulated event prevents default then we do the same on the donor.
var e = jQuery.extend(
new jQuery.Event(),
event,
{
type: type,
isSimulated: true,
originalEvent: {}
}
);
if ( bubble ) {
jQuery.event.trigger( e, null, elem );
} else {
jQuery.event.dispatch.call( elem, e );
}
if ( e.isDefaultPrevented() ) {
event.preventDefault();
}
}
}; jQuery.removeEvent = function( elem, type, handle ) {
if ( elem.removeEventListener ) {
elem.removeEventListener( type, handle, false );
}
};
//给Jquery类增加一个静态函数,当成类看时前面是命名空间,
jQuery.Event = function( src, props ) {
// 不是通过new调用,就new一个,
if ( !(this instanceof jQuery.Event) ) {
return new jQuery.Event( src, props );
} // Event object
if ( src && src.type ) {
this.originalEvent = src;
this.type = src.type; // Events bubbling up the document may have been marked as prevented
// by a handler lower down the tree; reflect the correct value.
this.isDefaultPrevented = ( src.defaultPrevented ||
src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; // Event type
} else {
this.type = src;
} // 属性继承
if ( props ) {
jQuery.extend( this, props );
} // Create a timestamp if incoming event doesn't have one
this.timeStamp = src && src.timeStamp || jQuery.now(); // Mark it as fixed
this[ jQuery.expando ] = true;
}; // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
jQuery.Event.prototype = {
isDefaultPrevented: returnFalse,
isPropagationStopped: returnFalse,
isImmediatePropagationStopped: returnFalse, preventDefault: function() {
var e = this.originalEvent; this.isDefaultPrevented = returnTrue; if ( e && e.preventDefault ) {
e.preventDefault();
}
},
stopPropagation: function() {//阻止冒泡
var e = this.originalEvent; this.isPropagationStopped = returnTrue; if ( e && e.stopPropagation ) {
e.stopPropagation();//原生的阻止冒泡方法
}
},
stopImmediatePropagation: function() {//阻止冒泡
this.isImmediatePropagationStopped = returnTrue;
this.stopPropagation();
}
}; // Create mouseenter/leave events using mouseover/out and event-time checks
// Support: Chrome 15+
jQuery.each({
mouseenter: "mouseover",
mouseleave: "mouseout"
}, function( orig, fix ) {
//挂载到special上
jQuery.event.special[ orig ] = {
delegateType: fix,
bindType: fix, handle: function( event ) {
var ret,
target = this,
related = event.relatedTarget,
handleObj = event.handleObj; // For mousenter/leave call the handler if related is outside the target.
// NB: No relatedTarget if the mouse left/entered the browser window
if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
event.type = handleObj.origType;
ret = handleObj.handler.apply( this, arguments );
event.type = fix;
}
return ret;
}
};
}); // Create "bubbling" focus and blur events
// Support: Firefox, Chrome, Safari
if ( !jQuery.support.focusinBubbles ) {
jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { // Attach a single capturing handler while someone wants focusin/focusout
var attaches = 0,
handler = function( event ) {
jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
};
//挂载到special上
jQuery.event.special[ fix ] = {
setup: function() {
if ( attaches++ === 0 ) {
document.addEventListener( orig, handler, true );
}
},
teardown: function() {
if ( --attaches === 0 ) {
document.removeEventListener( orig, handler, true );
}
}
};
});
} jQuery.fn.extend({ //$('ul').on('click','li',{name:'hello'},function(){}); 4个参数
//$('#div1').on('click',{name:'hello'},function(ev){}); 3个参数
//$('#div1').on('click','li',function(ev){}); 3个参数
//$('#div1').on('click',function(){}); 2个参数
on: function( types, selector, data, fn, one/*INTERNAL内部使用*/ ) {
var origFn, type;
/*
$('#div1').on({
'click' : function(){
alert(123);
},
'mouseover' : function(){
alert(456);
}
});
*/
if ( typeof types === "object" ) {//json
// ( types-Object, selector, data )
if ( typeof selector !== "string" ) {
// ( types-Object, data )
data = data || selector;
selector = undefined;
}
for ( type in types ) {//分开调用
this.on( type, selector, data, types[ type ], one );
}
return this;
} //行参修正
if ( data == null && fn == null ) {
// 2个参数,types=click,selector=function,data=fn=null
fn = selector;
data = selector = undefined;
} else if ( fn == null ) {//3个参数
if ( typeof selector === "string" ) {
//types=click,selector='li',data=function,fn=null
fn = data;
data = undefined;
} else {
//types=click,selector={}, data=function, fn=null
fn = data;
data = selector;
selector = undefined;
}
}
if ( fn === false ) {
fn = returnFalse;
} else if ( !fn ) {
return this;
} //$('#div1').one('click',function(){}) 只执行一次
if ( one === 1 ) {//one方法调进来的
origFn = fn;//村一次函数
fn = function( event ) {
jQuery().off( event );//取消事件
return origFn.apply( this, arguments );//函数执行一次
};
// 给函数加唯一标识
//a = 0; alert(a || (a=5)); 弹出5,a!=0时弹出a的值
fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
}
return this.each( function() {
//add( this, click, fn, {name:'hello'}, 'li' )
jQuery.event.add( this, types, fn, data, selector );
});
},
one: function( types, selector, data, fn ) {
return this.on( types, selector, data, fn, 1 );//第五个参数是1
},
off: function( types, selector, fn ) {//seletor是委托
var handleObj, type;
if ( types && types.preventDefault && types.handleObj ) {
// ( event ) dispatched jQuery.Event
handleObj = types.handleObj;
jQuery( types.delegateTarget ).off(
handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
handleObj.selector,
handleObj.handler
);
return this;
}
if ( typeof types === "object" ) {//json做遍历
// ( types-object [, selector] )
for ( type in types ) {
this.off( type, selector, types[ type ] );
}
return this;
}
//参数修正
if ( selector === false || typeof selector === "function" ) {
// ( types [, fn] )
fn = selector;
selector = undefined;
}
if ( fn === false ) {
fn = returnFalse;
}
//调用remove
return this.each(function() {
jQuery.event.remove( this, types, fn, selector );
});
}, trigger: function( type, data ) {
return this.each(function() {
jQuery.event.trigger( type, data, this );
});
},
triggerHandler: function( type, data ) {
var elem = this[0];
if ( elem ) {
return jQuery.event.trigger( type, data, elem, true );
}
}
});
var isSimple = /^.[^:#\[\.,]*$/,
rparentsprev = /^(?:parents|prev(?:Until|All))/,
rneedsContext = jQuery.expr.match.needsContext,
// methods guaranteed to produce a unique set when starting from a unique set
guaranteedUnique = {
children: true,
contents: true,
next: true,
prev: true
}; jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
"change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) { // Handle event binding
jQuery.fn[ name ] = function( data, fn ) {
return arguments.length > 0 ?
this.on( name, null, data, fn ) : //on没有selector就没有委托
this.trigger( name );
};
}); jQuery.fn.extend({
hover: function( fnOver, fnOut ) {
return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
}, bind: function( types, data, fn ) {//on没有委托
return this.on( types, null, data, fn );
},
unbind: function( types, fn ) {//on没有委托
return this.off( types, null, fn );
},
/*
$('ul').delegate('li','click',{name:'hello'},function(){
$(this).css('background','red');
});
*/
delegate: function( selector, types, data, fn ) {
return this.on( types, selector, data, fn );//this=$('ul'),on有selector就是委托
},
undelegate: function( selector, types, fn ) {
// ( namespace ) or ( selector, types [, fn] )
return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
}
}); }
///////////////////////////事件操作end//////////////////////////////////////////
///////////////////////////////////////DOM操作begin//////////////////////////////
{
jQuery.fn.extend({
//$('ul').find('li').css('background','red');
//$('ul').find( $('li') ).css('background','red');
find: function( selector ) {
var i,
ret = [],
self = this,
len = self.length;
if ( typeof selector !== "string" ) {//$('li')
//jQuery( selector )是li集合,filter调用者是li,filter返回的是满足function条件的li,li.prevObject=所有li,
//外层 this.pushStack返回满足条件的li,li.prevObject=所有ul,
return this.pushStack( jQuery( selector ).filter(function() {
for ( i = 0; i < len; i++ ) {
if ( jQuery.contains( self[ i ], this ) ) {//this是每一个li,是包含关系返回true
return true;
}
}
}) );
}
//$('ul').find('li').css('background','red');
//'li'
for ( i = 0; i < len; i++ ) {
jQuery.find( selector, self[ i ], ret );//Sizzle
} // Needed because $( selector, context ) becomes $( context ).find( selector ) , unique去重
ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
ret.selector = this.selector ? this.selector + " " + selector : selector;
return ret;
},
/*
<div class="box">div1<span class="box">span</span></div>
<div>div2</div>
<span class="box1">span1</span>
<span class="box2">span2</span>
<span class="box3">span3</span>
$('div').has('span').css('border','1px red solid');
*/
has: function( target ) {//target = "span"
/*jQuery( target, this ):
return ( context :div集合对象 || rootjQuery ).find( selector:"span" );Sizzle的find方法
*/
var targets = jQuery( target, this ),//this是div集合,返回从div集合对象中找到的span对象<span class="box">span</span>
l = targets.length;//1 return this.filter( function() {//this是div集合
var i = 0;
for ( ; i < l; i++ ) {
if ( jQuery.contains( this, targets[i] ) ) {//this是每一个div
return true;
}
}
}
/*
filter: function( f ) { //这里没有函数体
return this.pushStack( winnow(this, f || [], false) );//返回的是winnow(this, f || [], true)返回的结果并且结果.prevObject = 调用者。this是div集合
},
function winnow( elements, f, not ) {
//把grep当成主体函数来看
return jQuery.grep( elements, function( elem, i ) { //elements是div集合
return !!f.call( elem, i, elem ) !== not;//elem是每一个div
});//返回的是满足函数条件function也就是f的div集合
}
*/
);
}, not: function( selector ) {
//$('div').not('.box').css('border','1px red solid');
/*
pushStack: function( elems ) {
var ret = jQuery.merge( this.constructor(), elems );
ret.prevObject = this;
return ret; //返回的是满足条件的'.div',这个'.div'的prevObject=$('div'),用于回退
}
*/
//返回的是winnow(this, selector || [], true)返回的结果并且结果.prevObject = 调用者。
return this.pushStack( winnow(this, selector || [], true) );
}, filter: function( selector ) {
return this.pushStack( winnow(this, selector || [], false) );
}, is: function( selector ) {
return !!winnow(
this, // If this is a positional/relative selector, check membership in the returned set
// so $("p:first").is("p:last") won't return true for a doc with two "p".
typeof selector === "string" && rneedsContext.test( selector ) ?
jQuery( selector ) :
selector || [],
false
).length;
}, //$('#div2').closest('.box')
//$('#div2').closest('.box', $('body').get(0) )
closest: function( selectors, context ) {
var cur,
i = 0,
l = this.length,
matched = [],
pos = ( rneedsContext.test( selectors ) || typeof selectors !== "string" ) ?
//获取jqUERY对象
jQuery( selectors, context || this.context ) :
0; for ( ; i < l; i++ ) {
for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) {
// Always skip document fragments
if ( cur.nodeType < 11 && (pos ?
pos.index(cur) > -1 : // Don't pass non-elements to Sizzle
cur.nodeType === 1 &&
jQuery.find.matchesSelector(cur, selectors)) ) { cur = matched.push( cur );//找到就push进来
break;//找到一个就跳出循环
}
}
}
//入栈去重
return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched );
}, // Determine the position of an element within
// the matched set of elements
index: function( elem ) { //console.log( $('#div1').index() );//1,这个元素在所有兄弟节点的排名
if ( !elem ) {
//this.first() = this.eq(0);
return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
} //$('#span1').index('span') ;//span1在所有span中的排名
if ( typeof elem === "string" ) {
return core_indexOf.call( jQuery( elem ), this[ 0 ] );
} // $('span').index( $('#span1') ) );
return core_indexOf.call( this, // If it receives a jQuery object, the first element is used
elem.jquery ? elem[ 0 ] : elem
);
}, //$('div').add('span').css('border','1px red solid');//div span变红
add: function( selector, context ) {
var set = typeof selector === "string" ?
jQuery( selector, context ) :
jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
//this.get()是$('div')数组,set是'span'对象,合并成一个对象,
all = jQuery.merge( this.get(), set ); return this.pushStack( jQuery.unique(all) );
}, //$('div').find('span').css('color','red').addBack('.box').css('border','1px red solid');//addBack回到栈的下一层,并且当前层和下一层进行操作。
addBack: function( selector ) {
//this是span,this.prevObject是div,
return this.add( selector == null ?
this.prevObject : this.prevObject.filter(selector)
);
}
}); function sibling( cur, dir ) {
while ( (cur = cur[dir]) && cur.nodeType !== 1 ) {} return cur;
} //节点的获取
jQuery.each({
parent: function( elem ) {//elem是每一个调用者div
var parent = elem.parentNode;
return parent && parent.nodeType !== 11 ? parent : null;
},
parents: function( elem ) {
return jQuery.dir( elem, "parentNode" );
},
parentsUntil: function( elem, i, until ) {
return jQuery.dir( elem, "parentNode", until );
},
next: function( elem ) {
return sibling( elem, "nextSibling" );
},
prev: function( elem ) {
return sibling( elem, "previousSibling" );
},
nextAll: function( elem ) {
return jQuery.dir( elem, "nextSibling" );
},
prevAll: function( elem ) {
return jQuery.dir( elem, "previousSibling" );
},
nextUntil: function( elem, i, until ) {
return jQuery.dir( elem, "nextSibling", until );
},
prevUntil: function( elem, i, until ) {
return jQuery.dir( elem, "previousSibling", until );
},
siblings: function( elem ) {
return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
},
children: function( elem ) {
return jQuery.sibling( elem.firstChild );
},
contents: function( elem ) {//elem每一个div
return elem.contentDocument || jQuery.merge( [], elem.childNodes );
}
}, function( name, fn ) {
//jQuery.fn[ name ]是对jQuery.prototype对象的方法扩展
/*
map: function( elems, callback, arg ) {
if ( callback( elems[ i ], i, arg ) != null ) {
ret[ ret.length ] = callback( elems[ i ], i, arg );//末尾追加
}
return ret;
}
$('div').parentsUntil('body','div'), until, selector是'body','div',与上面parentsUntil定义的无关,
$('span').parents('body')
*/
jQuery.fn[ name ] = function( until, selector ) {
var matched = jQuery.map( this, fn, until );//this是div集合,返回fn(this[i],i,until)结果的集合
//具体操作都是在这里,真正实现是fn函数里面
if ( name.slice( -5 ) !== "Until" ) {
selector = until;//seletor='body'
} if ( selector && typeof selector === "string" ) {
matched = jQuery.filter( selector, matched );
} if ( this.length > 1 ) {
/*
guaranteedUnique = {
children: true,
contents: true,
next: true,
prev: true
};
*/
if ( !guaranteedUnique[ name ] ) {//在json里面
jQuery.unique( matched );//去重
} // Reverse order for parents* and prev-derivatives
if ( rparentsprev.test( name ) ) {
matched.reverse();
}
} return this.pushStack( matched );
};
}); jQuery.extend({
// jQuery.filter( '.box1', $('div'),true ) div中class有box1的
filter: function( expr, elems, not ) {
var elem = elems[ 0 ]; if ( not ) {
expr = ":not(" + expr + ")"; // :not(.box1)
} return elems.length === 1 && elem.nodeType === 1 ?
//Sizzle完成
jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] :
jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
return elem.nodeType === 1;
}));
}, dir: function( elem, dir, until ) {
var matched = [],
truncate = until !== undefined; while ( (elem = elem[ dir ]) && elem.nodeType !== 9 ) {
if ( elem.nodeType === 1 ) {
if ( truncate && jQuery( elem ).is( until ) ) {
break;
}
matched.push( elem );
}
}
return matched;
}, sibling: function( n, elem ) {
var matched = []; for ( ; n; n = n.nextSibling ) {
if ( n.nodeType === 1 && n !== elem ) {
matched.push( n );
}
} return matched;
}
}); //winnow($('div'), '.box1' || [], true) div中class有box1的
function winnow( elements, qualifier, not ) {//返回的是满足函数条件的elements元素集合
/*
$('div').filter(function(i,elem){
return elem.className == 'box'; }).css('border','1px red solid');
*/
if ( jQuery.isFunction( qualifier ) ) {
//grep是elements每一个元素执行function(),函数传入的实参一个是每一个元素另一个元素下标,这里elem, i是形参, 并判断函数的返回值,函数体这里定义,
return jQuery.grep( elements, function( elem, i ) {
//!!是把undefined和null转换成false
return !!qualifier.call( elem, i, elem ) !== not;
});//返回的是满足函数条件的elements:div元素集合 } /*
var oBox = document.getElementsByClassName('box')[0];
$('div').filter(oBox).css('border','1px red solid');
*/
if ( qualifier.nodeType ) {
return jQuery.grep( elements, function( elem ) {
return ( elem === qualifier ) !== not;
}); } if ( typeof qualifier === "string" ) {
//isSimple = /^.[^:#\[\.,]*$/ 任意字符开头,不包括:#[.,一直到结束
if ( isSimple.test( qualifier ) ) {//匹配成功:.box,#div1,:odd ,ul,li,div
return jQuery.filter( qualifier, elements, not );
}
//匹配不成功div:odd,ul #li,ul[title='hello'],div.box,ul,li
qualifier = jQuery.filter( qualifier, elements );
}
//匹配不成功
return jQuery.grep( elements, function( elem ) {
return ( core_indexOf.call( qualifier, elem ) >= 0 ) !== not;
});
} var rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
rtagName = /<([\w:]+)/,
rhtml = /<|&#?\w+;/,
rnoInnerhtml = /<(?:script|style|link)/i,
manipulation_rcheckableType = /^(?:checkbox|radio)$/i,
// checked="checked" or checked
rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
rscriptType = /^$|\/(?:java|ecma)script/i,
rscriptTypeMasked = /^true\/(.*)/,
rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g, // We have to close these tags to support XHTML (#13200)
wrapMap = { // Support: IE 9
option: [ 1, "<select multiple='multiple'>", "</select>" ], thead: [ 1, "<table>", "</table>" ],
col: [ 2, "<table><colgroup>", "</colgroup></table>" ],
tr: [ 2, "<table><tbody>", "</tbody></table>" ],
td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ], _default: [ 0, "", "" ]
}; // Support: IE 9
wrapMap.optgroup = wrapMap.option; wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
wrapMap.th = wrapMap.td; jQuery.fn.extend({
//$('div').text('<h1>标题</h1>');//不会被解析成标签
text: function( value ) {
return jQuery.access( this, function( value ) {
return value === undefined ?
jQuery.text( this ) ://获取
this.empty().append( ( this[ 0 ] && this[ 0 ].ownerDocument || document ).createTextNode( value ) );
}, null, value, arguments.length );
}, /*
$('span').append( oDiv );
$('span').append( $('div') );
$('span').append( '<div></div>' );
*/
append: function() {
return this.domManip( arguments, function( elem ) {
if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
var target = manipulationTarget( this, elem );
target.appendChild( elem );
}
});
}, /*
$('div').prepend( $('span') );//sopan添加到div里面的最前面
//<div><span></span>div</div>
*/
prepend: function() {
return this.domManip( arguments, function( elem ) {
if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
//manipulationTarget是做兼容,返回的是this
var target = manipulationTarget( this, elem );
target.insertBefore( elem, target.firstChild );//firstChild第一个子节点
}
});
}, before: function() {
return this.domManip( arguments, function( elem ) {
if ( this.parentNode ) {
//添加到this前面
this.parentNode.insertBefore( elem, this );
}
});
}, after: function() {
return this.domManip( arguments, function( elem ) {
if ( this.parentNode ) {
//添加到下一个兄弟节点前面。就是后面
this.parentNode.insertBefore( elem, this.nextSibling );
}
});
}, // $('div').remove('.box');//删除节点
remove: function( selector, keepData ) {
var elem,
elems = selector ? jQuery.filter( selector, this ) : this,
i = 0; for ( ; (elem = elems[i]) != null; i++ ) {
if ( !keepData && elem.nodeType === 1 ) {
//gatAll删除子标签
jQuery.cleanData( getAll( elem ) );//删除节点并且删除点击事件
} if ( elem.parentNode ) {
//标签中有script标签
if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) {
setGlobalEval( getAll( elem, "script" ) );
}
elem.parentNode.removeChild( elem );//删除这个节点,原生方法
}
} return this;
}, empty: function() {
var elem,
i = 0; for ( ; (elem = this[i]) != null; i++ ) {
if ( elem.nodeType === 1 ) { // 清空data数据缓存和点击事件
jQuery.cleanData( getAll( elem, false ) ); // Remove any remaining nodes
elem.textContent = "";
}
} return this;
}, //var cloneDiv3 = $('div').clone(true,true);//子元素有点击事件
clone: function( dataAndEvents, deepDataAndEvents ) {
dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
/*
(this.) map: function( callback ) {
return this.pushStack( jQuery.map(
this,
function( elem, i ) {
return callback.call( elem, i, elem );
}
)
);
},
*/
return this.map( function () {//this的每一个元素,调用f,返回f结果集合,结果.preObject= $('div'), 调用内部clone方法
return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
});
}, html: function( value ) {
return jQuery.access( this, function( value ) {
var elem = this[ 0 ] || {},
i = 0,
l = this.length; if ( value === undefined && elem.nodeType === 1 ) {
return elem.innerHTML;//$('span').html();原生获取方式
} //设置$('span').html('<tr></tr>');
// See if we can take a shortcut and just use innerHTML
if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
!wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { value = value.replace( rxhtmlTag, "<$1></$2>" ); try {
for ( ; i < l; i++ ) {
elem = this[ i ] || {}; // Remove element nodes and prevent memory leaks
if ( elem.nodeType === 1 ) {
jQuery.cleanData( getAll( elem, false ) );
elem.innerHTML = value;
}
} elem = 0; // If using innerHTML throws an exception, use the fallback method
} catch( e ) {}
} if ( elem ) {
this.empty().append( value );//append添加节点
}
}, null, value, arguments.length );
}, replaceWith: function() {
var
// Snapshot the DOM in case .domManip sweeps something relevant into its fragment
args = jQuery.map( this, function( elem ) {
return [ elem.nextSibling, elem.parentNode ];
}),
i = 0; // Make the changes, replacing each context element with the new content
this.domManip( arguments, function( elem ) {
var next = args[ i++ ],
parent = args[ i++ ]; if ( parent ) {
// Don't use the snapshot next if it has moved (#13810)
if ( next && next.parentNode !== parent ) {
next = this.nextSibling;
}
jQuery( this ).remove();
parent.insertBefore( elem, next );
}
// Allow new content to include elements from the context set
}, true ); // Force removal if there was no new content (e.g., from empty arguments)
return i ? this : this.remove();
}, //$('div').detach();
detach: function( selector ) {
return this.remove( selector, true );
}, domManip: function( args, callback, allowIntersection ) { // Flatten any nested arrays
args = core_concat.apply( [], args ); var fragment, first, scripts, hasScripts, node, doc,
i = 0,
l = this.length,
set = this,
iNoClone = l - 1,
value = args[ 0 ],
isFunction = jQuery.isFunction( value );
/*
$('span').append(function(){
return 'hello';
});
*/
// We can't cloneNode fragments that contain checked, in WebKit
if ( isFunction || !( l <= 1 || typeof value !== "string" || jQuery.support.checkClone || !rchecked.test( value ) ) ) {
return this.each(function( index ) {
var self = set.eq( index );
if ( isFunction ) {
args[ 0 ] = value.call( this, index, self.html() );
}
//重新调用$('span').append('hello')
self.domManip( args, callback, allowIntersection );
});
} if ( l ) {
//$('span').append('<h1>hello</h1>'); 文档碎片
fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, !allowIntersection && this );
first = fragment.firstChild; if ( fragment.childNodes.length === 1 ) {
fragment = first;
} if ( first ) {
scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
hasScripts = scripts.length; // Use the original fragment for the last item instead of the first because it can end up
// being emptied incorrectly in certain situations (#8070).
for ( ; i < l; i++ ) {
node = fragment; if ( i !== iNoClone ) {
node = jQuery.clone( node, true, true ); // Keep references to cloned scripts for later restoration
if ( hasScripts ) {
// Support: QtWebKit
// jQuery.merge because core_push.apply(_, arraylike) throws
jQuery.merge( scripts, getAll( node, "script" ) );
}
} callback.call( this[ i ], node, i );
} if ( hasScripts ) {
doc = scripts[ scripts.length - 1 ].ownerDocument; // Reenable scripts
jQuery.map( scripts, restoreScript ); // Evaluate executable scripts on first document insertion
for ( i = 0; i < hasScripts; i++ ) {
node = scripts[ i ];
if ( rscriptType.test( node.type || "" ) &&
!data_priv.access( node, "globalEval" ) && jQuery.contains( doc, node ) ) { if ( node.src ) {
// Hope ajax is available...
jQuery._evalUrl( node.src );
} else {
jQuery.globalEval( node.textContent.replace( rcleanScript, "" ) );
}
}
}
}
}
} return this;
}
}); jQuery.each({
appendTo: "append",
prependTo: "prepend",
insertBefore: "before",
insertAfter: "after",
replaceAll: "replaceWith"
}, function( name, original ) {
//$('span').appendTo( $('div') ).css('border','1px red solid');
jQuery.fn[ name ] = function( selector ) {
var elems,
ret = [],
insert = jQuery( selector ),//$('div')
last = insert.length - 1,
i = 0; for ( ; i <= last; i++ ) {
elems = i === last ? this : this.clone( true );
//调用jQuery($('div')[i]).append($('span'))
jQuery( insert[ i ] )[ original ]( elems ); // Support: QtWebKit
// .get() because core_push.apply(_, arraylike) throws
core_push.apply( ret, elems.get() );
} return this.pushStack( ret );
};
}); jQuery.extend({
//jQuery.clone( $('div')每一个, true, true );
clone: function( elem, dataAndEvents, deepDataAndEvents ) {
var i, l, srcElements, destElements,
clone = elem.cloneNode( true ),//原生的方式,克隆,仅仅复制html节点包括子节点,事件缓存没有复制过来
//当前页
inPage = jQuery.contains( elem.ownerDocument, elem );
/*
console.log(clone);//节点包括子节点。false就只有外层节点,没有子节点
var dd = getAll( elem );//[div, span, p]
var rr = getAll( clone );//[div, span, p]
*/
/* Support: IE >= 9 if是兼容性 $('input').prop('checked',true);
$('input').clone().appendTo( 'body' );//jQuery克隆过去也是选中的,但是原生的是没有选中的,
*/
if ( !jQuery.support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && !jQuery.isXMLDoc( elem ) ) { // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2
destElements = getAll( clone );//数组
srcElements = getAll( elem ); for ( i = 0, l = srcElements.length; i < l; i++ ) {
fixInput( srcElements[ i ], destElements[ i ] );
}
} // Copy the events from the original to the clone
if ( dataAndEvents ) {//克隆自身的事件
if ( deepDataAndEvents ) {//克隆子项的事件
srcElements = srcElements || getAll( elem );
destElements = destElements || getAll( clone ); for ( i = 0, l = srcElements.length; i < l; i++ ) {
cloneCopyEvent( srcElements[ i ], destElements[ i ] );
}
} else {//不克隆子项只克隆自身,原节点、复制的节点
cloneCopyEvent( elem, clone );
}
} // 克隆的元素包含script标签
destElements = getAll( clone, "script" );
if ( destElements.length > 0 ) {
//把script标签设置为全局script执行
setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
} // Return the cloned set
return clone;
}, //创建文档碎片
buildFragment: function( elems, context, scripts, selection ) {
var elem, tmp, tag, wrap, contains, j,
i = 0,
l = elems.length,
fragment = context.createDocumentFragment(),
nodes = [];//实现的结果 for ( ; i < l; i++ ) {
elem = elems[ i ];//每一个元素做处理 if ( elem || elem === 0 ) { // Add nodes directly
if ( jQuery.type( elem ) === "object" ) {
// Support: QtWebKit
// jQuery.merge because core_push.apply(_, arraylike) throws
jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); // Convert non-html into a text node
} else if ( !rhtml.test( elem ) ) {
nodes.push( context.createTextNode( elem ) ); //$('span').append( '<h1>hello</h1><h1>hello</h1><h1>hello</h1>' ); 添加多标签用到文档碎片,效率高,
} else {
tmp = tmp || fragment.appendChild( context.createElement("div") ); // Deserialize a standard representation
tag = ( rtagName.exec( elem ) || ["", ""] )[ 1 ].toLowerCase();
wrap = wrapMap[ tag ] || wrapMap._default;
tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[ 2 ];
/*
wrapMap = { // Support: IE 9
option: [ 1, "<select multiple='multiple'>", "</select>" ], thead: [ 1, "<table>", "</table>" ],
col: [ 2, "<table><colgroup>", "</colgroup></table>" ],
tr: [ 2, "<table><tbody>", "</tbody></table>" ],
td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ], _default: [ 0, "", "" ]
};
*/
j = wrap[ 0 ];
while ( j-- ) {
tmp = tmp.lastChild;
} // Support: QtWebKit
// jQuery.merge because core_push.apply(_, arraylike) throws
jQuery.merge( nodes, tmp.childNodes ); // Remember the top-level container
tmp = fragment.firstChild; // Fixes #12346
// Support: Webkit, IE
tmp.textContent = "";
}
}
} // Remove wrapper from fragment
fragment.textContent = ""; i = 0;
while ( (elem = nodes[ i++ ]) ) { // #4087 - If origin and destination elements are the same, and this is
// that element, do not do anything
if ( selection && jQuery.inArray( elem, selection ) !== -1 ) {
continue;
} contains = jQuery.contains( elem.ownerDocument, elem ); // Append to fragment
tmp = getAll( fragment.appendChild( elem ), "script" ); // Preserve script evaluation history
if ( contains ) {
setGlobalEval( tmp );
} // Capture executables
if ( scripts ) {
j = 0;
while ( (elem = tmp[ j++ ]) ) {
if ( rscriptType.test( elem.type || "" ) ) {
scripts.push( elem );
}
}
}
} return fragment;
}, //清除元素的点击事件和数据(data保存的)
cleanData: function( elems ) {
var data, elem, events, type, key, j,
special = jQuery.event.special,
i = 0; for ( ; (elem = elems[ i ]) !== undefined; i++ ) {
if ( Data.accepts( elem ) ) {
key = elem[ data_priv.expando ];
//删除元素的点击事件,data里的事件
if ( key && (data = data_priv.cache[ key ]) ) {
events = Object.keys( data.events || {} );
if ( events.length ) {
for ( j = 0; (type = events[j]) !== undefined; j++ ) {
if ( special[ type ] ) {//特殊事件,自定义事件
jQuery.event.remove( elem, type ); // This is a shortcut to avoid jQuery.event.remove's overhead
} else {
jQuery.removeEvent( elem, type, data.handle );
}
}
}
if ( data_priv.cache[ key ] ) {//删除缓存data里的
// Discard any remaining `private` data
delete data_priv.cache[ key ];
}
}
}
// Discard any remaining `user` data
delete data_user.cache[ elem[ data_user.expando ] ];
}
}, _evalUrl: function( url ) {
return jQuery.ajax({
url: url,
type: "GET",
dataType: "script",
async: false,
global: false,
"throws": true
});
}
}); // Support: 1.x compatibility
// Manipulating tables requires a tbody
function manipulationTarget( elem, content ) {
return jQuery.nodeName( elem, "table" ) &&
jQuery.nodeName( content.nodeType === 1 ? content : content.firstChild, "tr" ) ?
//没有tbody自动添加tbody
elem.getElementsByTagName("tbody")[0] ||
elem.appendChild( elem.ownerDocument.createElement("tbody") ) :
elem;
} // Replace/restore the type attribute of script elements for safe DOM manipulation
function disableScript( elem ) {
elem.type = (elem.getAttribute("type") !== null) + "/" + elem.type;
return elem;
}
function restoreScript( elem ) {
var match = rscriptTypeMasked.exec( elem.type ); if ( match ) {
elem.type = match[ 1 ];
} else {
elem.removeAttribute("type");
} return elem;
} // Mark scripts as having already been evaluated
function setGlobalEval( elems, refElements ) {
var l = elems.length,
i = 0; for ( ; i < l; i++ ) {
data_priv.set(
elems[ i ], "globalEval", !refElements || data_priv.get( refElements[ i ], "globalEval" )
);
}
} //cloneCopyEvent( elem, clone ); 克隆事件,原节点目的节点,节点已经由原生方法克隆了,
function cloneCopyEvent( src, dest ) {
var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; if ( dest.nodeType !== 1 ) {
return;
} // 1. 从data中克隆事件
if ( data_priv.hasData( src ) ) {
pdataOld = data_priv.access( src );
pdataCur = data_priv.set( dest, pdataOld );
events = pdataOld.events; if ( events ) {
delete pdataCur.handle;
pdataCur.events = {};
//遍历
for ( type in events ) {
for ( i = 0, l = events[ type ].length; i < l; i++ ) {
jQuery.event.add( dest, type, events[ type ][ i ] );
}
}
}
} // 2. 从data中克隆数据
if ( data_user.hasData( src ) ) {
udataOld = data_user.access( src );
udataCur = jQuery.extend( {}, udataOld );
//遍历
data_user.set( dest, udataCur );
}
} function getAll( context, tag ) {
var ret = context.getElementsByTagName ? context.getElementsByTagName( tag || "*" ) :
context.querySelectorAll ? context.querySelectorAll( tag || "*" ) :
[]; return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
jQuery.merge( [ context ], ret ) :
ret;
} // Support: IE >= 9
function fixInput( src, dest ) {
var nodeName = dest.nodeName.toLowerCase(); // Fails to persist the checked state of a cloned checkbox or radio button.
if ( nodeName === "input" && manipulation_rcheckableType.test( src.type ) ) {
dest.checked = src.checked; // Fails to return the selected option to the default selected state when cloning options
} else if ( nodeName === "input" || nodeName === "textarea" ) {
dest.defaultValue = src.defaultValue;
}
}
jQuery.fn.extend({
/*
$('span').wrap('<div>');//每个span包一个div $('span').wrapAll('<div>');//所有span只包一个div,如果span里面有其他标签会把这个标签移出来
$('span').wrapInner('<div>');//每个span里面包一个div
$('span').unwrap();//删除父级
*/
wrapAll: function( html ) {
var wrap; if ( jQuery.isFunction( html ) ) {
/*
$('span').wrapAll(function(){
return '<div>';
});
*/
return this.each(function( i ) {//函数执行一次,然后用函数返回值再调用
jQuery( this ).wrapAll( html.call(this, i) );
});
} if ( this[ 0 ] ) { // $('span').wrapAll('<div>'); 创建div对象并且克隆节点,事件也克隆
wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); if ( this[ 0 ].parentNode ) {
wrap.insertBefore( this[ 0 ] );//添加到第一个span的前面
} wrap.map(function() {
var elem = this; while ( elem.firstElementChild ) {
elem = elem.firstElementChild;
} return elem;
}).append( this );
} return this;
}, wrapInner: function( html ) {
if ( jQuery.isFunction( html ) ) {
return this.each(function( i ) {
jQuery( this ).wrapInner( html.call(this, i) );
});
} return this.each(function() {
var self = jQuery( this ),
contents = self.contents(); if ( contents.length ) {
contents.wrapAll( html ); } else {
self.append( html );
}
});
},
//分别包装
wrap: function( html ) {
var isFunction = jQuery.isFunction( html ); return this.each(function( i ) {
jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
});
},
//找到父级,替换操作,删除父级,不能是body
unwrap: function() {
return this.parent().each(function() {
if ( !jQuery.nodeName( this, "body" ) ) {
jQuery( this ).replaceWith( this.childNodes );
}
}).end();
}
}); } ////////////////////////////////////////////DOM操作end/////////////////////////// ////////////////////////////////////css操作开始////////////////////////////////
{
var curCSS, iframe,
// swappable if display is none or starts with table except "table", "table-cell", or "table-caption"
// see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
rdisplayswap = /^(none|table(?!-c[ea]).+)/,
rmargin = /^margin/,
rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ),
rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ),
rrelNum = new RegExp( "^([+-])=(" + core_pnum + ")", "i" ),
elemdisplay = { BODY: "block" }, cssShow = { position: "absolute", visibility: "hidden", display: "block" },
cssNormalTransform = {
letterSpacing: 0,
fontWeight: 400
}, cssExpand = [ "Top", "Right", "Bottom", "Left" ],
cssPrefixes = [ "Webkit", "O", "Moz", "ms" ]; // return a css property mapped to a potentially vendor prefixed property
function vendorPropName( style, name ) { // shortcut for names that are not vendor prefixed
if ( name in style ) {
return name;
} // check for vendor prefixed names
var capName = name.charAt(0).toUpperCase() + name.slice(1),
origName = name,
i = cssPrefixes.length; while ( i-- ) {
name = cssPrefixes[ i ] + capName;
if ( name in style ) {
return name;
}
} return origName;
} function isHidden( elem, el ) {
// isHidden might be called from jQuery#filter function;
// in that case, element will be second argument
elem = el || elem;
return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );//动态创建的元素也是不包含也是隐藏
} // NOTE: we've included the "window" in window.getComputedStyle
// because jsdom on node.js will break without it.
function getStyles( elem ) {
return window.getComputedStyle( elem, null );//封装
} function showHide( elements, show ) {
var display, elem, hidden,
values = [],
index = 0,
length = elements.length; for ( ; index < length; index++ ) {
elem = elements[ index ];
if ( !elem.style ) {
continue;
} values[ index ] = data_priv.get( elem, "olddisplay" );
display = elem.style.display;
if ( show ) {
// Reset the inline display of this element to learn if it is
// being hidden by cascaded rules or not
if ( !values[ index ] && display === "none" ) {
elem.style.display = "";
} // Set elements which have been overridden with display: none
// in a stylesheet to whatever the default browser style is
// for such an element
if ( elem.style.display === "" && isHidden( elem ) ) {
values[ index ] = data_priv.access( elem, "olddisplay", css_defaultDisplay(elem.nodeName) );
}
} else { if ( !values[ index ] ) {
hidden = isHidden( elem ); if ( display && display !== "none" || !hidden ) {
//获取元素原来的显示样式,块级元素是block行内元素是inline
data_priv.set( elem, "olddisplay", hidden ? display : jQuery.css(elem, "display") );
}
}
}
} //每一个元素遍历
for ( index = 0; index < length; index++ ) {
elem = elements[ index ];
if ( !elem.style ) {//有sytle属性通过style属性隐藏显示,没有就跳出。
continue;
}
if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
//values[ index ] || "",values[ index ]不存在时给空就是false,不直接写 = show ? 'block' :'none',因为是行内元素就不能是block而是in-line,
//元素一上来是隐藏状态是获取不到元素的display属性是块级还是行内,调用show的时候通过elem.nodeName动态创建,获取元素是块级还是行内。
elem.style.display = show ? values[ index ] || "" : "none";
}
} return elements;
} jQuery.fn.extend({
//$('#div1').css('color','yellow');
css: function( name, value ) {
return jQuery.access( this, function( elem, name, value ) {
var styles, len,
map = {},
i = 0;
//$('#div1').css( ['color','backgroundColor','width'] )
if ( jQuery.isArray( name ) ) {//数组获取
styles = getStyles( elem );
len = name.length; for ( ; i < len; i++ ) {
//调用jQuery.css()方法来获取
//jQuery.css方法多个值获取传了4个参数,第三个参数是false相当于没传,只是占位隔开第四个参数,
map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
} return map;
} return value !== undefined ?
jQuery.style( elem, name, value ) ://设置:$('#div1').css('color','yellow')
jQuery.css( elem, name );//获取:$('#div1').css('color'),单个值获取只传了2个参数,后面2个参数没传相当于是false,
}, name, value, arguments.length > 1 );
},
show: function() {
return showHide( this, true );
},
hide: function() {
return showHide( this );//没传就是false
},
toggle: function( state ) {
if ( typeof state === "boolean" ) {
return state ? this.show() : this.hide();
} return this.each(function() {
if ( isHidden( this ) ) {//jQuery对象里面每一个元素是js节点对象
jQuery( this ).show();
} else {
jQuery( this ).hide();
}
});
}
}); jQuery.extend({
// Add in style property hooks for overriding the default
// behavior of getting and setting a style property
cssHooks: {
opacity: {//透明度处理
get: function( elem, computed ) {
if ( computed ) {
// We should always get a number back from opacity
var ret = curCSS( elem, "opacity" );
return ret === "" ? "1" : ret;
}
}
}
}, // Don't automatically add "px" to these possibly-unitless properties
cssNumber: {
"columnCount": true,
"fillOpacity": true,
"fontWeight": true,
"lineHeight": true,
"opacity": true,
"order": true,
"orphans": true,
"widows": true,
"zIndex": true,
"zoom": true
}, // Add in properties whose names you wish to fix before
// setting or getting the value
cssProps: {
// normalize float css property
"float": "cssFloat"
}, //设置:jQuery.style( $('#div1')[i], 'float','left' )
style: function( elem, name, value, extra ) {
//元素节点的节点类型是3或者8,
if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
return;
}
// Make sure that we're working with the right name
var ret, type, hooks,
origName = jQuery.camelCase( name ),//转驼峰,background-color --> backgroundColor
style = elem.style;//元素的所有style
//有就返回,没有就加入cssProps这个json,全部保存在$这个对象中。将float转成cssfloat,
name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );
// gets hook for the prefixed version
// followed by the unprefixed version
hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
// Check if we're setting a value
if ( value !== undefined ) {//设置
type = typeof value;
// convert relative number strings (+= or -=) to relative numbers. #7345
//$('#div1').css('width','+=100');
if ( type === "string" && (ret = rrelNum.exec( value )) ) {
value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
// Fixes bug #9237
type = "number";
}
// value是空或者无限大,设置时候就返回,
if ( value == null || type === "number" && isNaN( value ) ) {
return;
}
// origName不在jQuery.cssNumber中,在jQuery.cssNumber中不需要加单位。
if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
value += "px";//加单位
} // Fixes #8908, it can be done more correctly by specifying setters in cssHooks,
// but it would mean to define eight (for every problematic property) identical functions
if ( !jQuery.support.clearCloneStyle && value === "" && name.indexOf("background") === 0 ) {
style[ name ] = "inherit";
} // If a hook was provided, use that value, otherwise just set the specified value
if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) {
style[ name ] = value;//没有兼容性,通过style.name = value设置
} } else {//获取
// If a hook was provided get the non-computed value from there
if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
return ret;
} // 没有兼容性,通过style.name获取
return style[ name ];
}
}, //获取:jQuery.css( $('#div1')[i], 'color' );
css: function( elem, name, extra, styles ) {
var val, num, hooks,
//$('#div1').css('background-color'); odiv.style.background-color是不行的,转驼峰成backgroundColor
origName = jQuery.camelCase( name );//转驼峰, /*
cssProps: {
"float": "cssFloat" //class js中通过 className代表
},
*/
//有就返回,没有就加入cssProps这个json,全部保存在$这个对象中。
name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );
// gets hook for the prefixed version
// followed by the unprefixed version
hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; // 获取值的兼容性处理
if ( hooks && "get" in hooks ) {
val = hooks.get( elem, true, extra );
} // 不做兼容性处理调用curCSS方法获取
if ( val === undefined ) {
val = curCSS( elem, name, styles );
} /*
cssNormalTransform = {
letterSpacing: 0,
fontWeight: 400
},
返回的值是normal,并且属性名是letterSpacing、fontWeight就返回0、40
*/
if ( val === "normal" && name in cssNormalTransform ) {
val = cssNormalTransform[ name ];
} // 额外参数做判断
if ( extra === "" || extra ) {
num = parseFloat( val );//123px转换成123
return extra === true || jQuery.isNumeric( num ) ? num || 0 : val;
}
return val;
}
}); //curCSS( $('#div1')[i], 'color')
curCSS = function( elem, name, _computed ) {
var width, minWidth, maxWidth,
computed = _computed || getStyles( elem ),//getStyles调用原生window.getComputedStyle( elem, null );提高性能 // Support: IE9
// getPropertyValue is only needed for .css('filter') in IE9, see #12537
ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined,
style = elem.style; if ( computed ) {
//ownerDocument获取元素所在页面的document,动态创建一个元素var $span = $('<span>') 就不包含,调用jQuery.style方法来获取,
if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
ret = jQuery.style( elem, name );
} // Support: Safari 5.1
// A tribute to the "awesome hack by Dean Edwards"
// Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels
// this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) { // Remember the original values
width = style.width;
minWidth = style.minWidth;
maxWidth = style.maxWidth; // Put in the new values to get a computed value out
style.minWidth = style.maxWidth = style.width = ret;
ret = computed.width; // Revert the changed values
style.width = width;
style.minWidth = minWidth;
style.maxWidth = maxWidth;
}
} return ret;
}; function setPositiveNumber( elem, value, subtract ) {
var matches = rnumsplit.exec( value );
return matches ?
// Guard against undefined "subtract", e.g., when used as in cssHooks
Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) :
value;
} function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
var i = extra === ( isBorderBox ? "border" : "content" ) ?
// If we already have the right measurement, avoid augmentation
4 :
// Otherwise initialize for horizontal or vertical properties
name === "width" ? 1 : 0, val = 0; for ( ; i < 4; i += 2 ) {
// both box models exclude margin, so add it if we want it
if ( extra === "margin" ) {
val += jQuery.css( elem, extra + cssExpand[ i ], true, styles );
} if ( isBorderBox ) {
// border-box includes padding, so remove it if we want content
if ( extra === "content" ) {
val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
} // at this point, extra isn't border nor margin, so remove border
if ( extra !== "margin" ) {
val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
}
} else {
// at this point, extra isn't content, so add padding
val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); // at this point, extra isn't content nor padding, so add border
if ( extra !== "padding" ) {
val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
}
}
} return val;
} function getWidthOrHeight( elem, name, extra ) { // Start with offset property, which is equivalent to the border-box value
var valueIsBorderBox = true,
val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
styles = getStyles( elem ),
isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; // some non-html elements return undefined for offsetWidth, so check for null/undefined
// svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
// MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
if ( val <= 0 || val == null ) {
// Fall back to computed then uncomputed css if necessary
val = curCSS( elem, name, styles );
if ( val < 0 || val == null ) {
val = elem.style[ name ];
} // Computed unit is not pixels. Stop here and return.
if ( rnumnonpx.test(val) ) {
return val;
} // we need the check for style in case a browser which returns unreliable values
// for getComputedStyle silently falls back to the reliable elem.style
valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] ); // Normalize "", auto, and prepare for extra
val = parseFloat( val ) || 0;
} // use the active box-sizing model to add/subtract irrelevant styles
return ( val +
augmentWidthOrHeight(
elem,
name,
extra || ( isBorderBox ? "border" : "content" ),
valueIsBorderBox,
styles
)
) + "px";
} //元素一上来是隐藏状态是获取不到元素的display属性是块级还是行内,调用show的时候通过elem.nodeName动态创建,获取元素是块级还是行内。
//css_defaultDisplay(elem.nodeName) elem.nodeName=div或者span
function css_defaultDisplay( nodeName ) {
var doc = document,
/*
elemdisplay = { BODY: "block" },
*/
display = elemdisplay[ nodeName ]; if ( !display ) {//元素是body就是block,不是body就动态创建然后获取display属性,因为body不能动态创建
display = actualDisplay( nodeName, doc );//动态创建 // iframe情况
if ( display === "none" || !display ) {
//动态创建iframe
iframe = ( iframe ||
jQuery("<iframe frameborder='0' width='0' height='0'/>")
.css( "cssText", "display:block !important" )
).appendTo( doc.documentElement ); // Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse
doc = ( iframe[0].contentWindow || iframe[0].contentDocument ).document;
doc.write("<!doctype html><html><body>");
doc.close();
//获取iframe里面的节点的display属性
display = actualDisplay( nodeName, doc );
iframe.detach();
} // Store the correct default display
elemdisplay[ nodeName ] = display;
} return display;
} // 动态创建元素actualDisplay( span, document )
function actualDisplay( name, doc ) {
var elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ),//创建添加到body中,动态创建元素肯定是显示的,
display = jQuery.css( elem[0], "display" );//获取display值
elem.remove();
return display;
} jQuery.each([ "height", "width" ], function( i, name ) {
jQuery.cssHooks[ name ] = {
get: function( elem, computed, extra ) {
if ( computed ) {
// certain elements can have dimension info if we invisibly show them
// however, it must have a current display style that would benefit from this
return elem.offsetWidth === 0 && rdisplayswap.test( jQuery.css( elem, "display" ) ) ?
jQuery.swap( elem, cssShow, function() {
return getWidthOrHeight( elem, name, extra );
}) :
getWidthOrHeight( elem, name, extra );
}
}, set: function( elem, value, extra ) {
var styles = extra && getStyles( elem );
return setPositiveNumber( elem, value, extra ?
augmentWidthOrHeight(
elem,
name,
extra,
jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
styles
) : 0
);
}
};
}); // These hooks cannot be added until DOM ready because the support test
// for it is not run until after DOM ready
jQuery(function() {
// Support: Android 2.3
if ( !jQuery.support.reliableMarginRight ) {
jQuery.cssHooks.marginRight = {
get: function( elem, computed ) {
if ( computed ) {
// Support: Android 2.3
// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
// Work around by temporarily setting element display to inline-block
return jQuery.swap( elem, { "display": "inline-block" },
curCSS, [ elem, "marginRight" ] );
}
}
};
} // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
// getComputedStyle returns percent when specified for top/left/bottom/right
// rather than make the css module depend on the offset module, we just check for it here
if ( !jQuery.support.pixelPosition && jQuery.fn.position ) {
jQuery.each( [ "top", "left" ], function( i, prop ) {
jQuery.cssHooks[ prop ] = {
get: function( elem, computed ) {
if ( computed ) {
computed = curCSS( elem, prop );
// if curCSS returns percentage, fallback to offset
return rnumnonpx.test( computed ) ?
jQuery( elem ).position()[ prop ] + "px" :
computed;
}
}
};
});
} }); if ( jQuery.expr && jQuery.expr.filters ) {
jQuery.expr.filters.hidden = function( elem ) {
// Support: Opera <= 12.12
// Opera reports offsetWidths and offsetHeights less than zero on some elements
return elem.offsetWidth <= 0 && elem.offsetHeight <= 0;
}; jQuery.expr.filters.visible = function( elem ) {
return !jQuery.expr.filters.hidden( elem );
};
} // These hooks are used by animate to expand properties
jQuery.each({
margin: "",
padding: "",
border: "Width"
}, function( prefix, suffix ) {
jQuery.cssHooks[ prefix + suffix ] = {
expand: function( value ) {
var i = 0,
expanded = {}, // assumes a single number if not a string
parts = typeof value === "string" ? value.split(" ") : [ value ]; for ( ; i < 4; i++ ) {
expanded[ prefix + cssExpand[ i ] + suffix ] =
parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
} return expanded;
}
}; if ( !rmargin.test( prefix ) ) {
jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
}
});
} //////////////////////////css操作结束////////////////////////////////////////////////////////////////////
//////////////////////////ajax开始////////////////////////////////////////////////////////////////////
{
var r20 = /%20/g, //全部空格
rbracket = /\[\]$/, //结尾位置匹配中括号
rCRLF = /\r?\n/g,
rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
rsubmittable = /^(?:input|select|textarea|keygen)/i; jQuery.fn.extend({
serialize: function() {
return jQuery.param( this.serializeArray() );
},
// console.log($('#form1').serializeArray());//[{'name':'a','value':'1'},{'name':'b','value':'2'}]
serializeArray: function() {
return this.map(function(){//map是数组都执行函数,返回结果集合
//只有表单有elements属性。
var elements = jQuery.prop( this, "elements" );
return elements ? jQuery.makeArray( elements ) : this;
})
.filter(function(){
var type = this.type;
// Use .is(":disabled") so that fieldset[disabled] works
return this.name && !jQuery( this ).is( ":disabled" ) &&
rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
( this.checked || !manipulation_rcheckableType.test( type ) );
})
.map(function( i, elem ){
var val = jQuery( this ).val(); return val == null ?
null :
jQuery.isArray( val ) ?
jQuery.map( val, function( val ){
return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
}) :
{ name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
}).get();
}
}); //json对象、数组对象序列化成字符串
//$.param( [ {'name':1,'value':2} ] );//1=2,name为key,value为value
jQuery.param = function( a, traditional ) {
var prefix,
s = [],
//定义一个函数
add = function( key, value ) {
//是函数就让函数执行,得到返回值
value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
//编码
s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
}; // Set traditional to true for jQuery <= 1.3.2 behavior.
if ( traditional === undefined ) {
traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
} /*
var $a = $( [ {'name':1,'value':2},{'name':3,'value':4} ] )};
console.log( $.param($a) )
*/
if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
// Serialize the form elements
jQuery.each( a, function() {
add( this.name, this.value );//每一个a的name和value属性,
}); } else {
// a={'name':[1,3],'value':2},a={'name':{'a1':1,'a3':3},'value':2} add是回调进行拼接
for ( prefix in a ) {
buildParams( prefix, a[ prefix ], traditional, add );
}
} // 通过&链接,
return s.join( "&" ).replace( r20, "+" );
}; //buildParams(name,[1,3],traditional,add())
////buildParams(name,{'a1':1,'a3':3},traditional,add())
function buildParams( prefix, obj, traditional, add ) {
var name; if ( jQuery.isArray( obj ) ) {
jQuery.each( obj, function( i, v ) {
if ( traditional || rbracket.test( prefix ) ) {//传统方式
add( prefix, v ); } else {
// 多用三目运算符
buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
}
}); } else if ( !traditional && jQuery.type( obj ) === "object" ) {
// Serialize object item.
for ( name in obj ) {
buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
} } else {
// 不是数组不是对象,普通方式
add( prefix, obj );
}
} var
// Document location
ajaxLocParts,
ajaxLocation, ajax_nonce = jQuery.now(), ajax_rquery = /\?/,
rhash = /#.*$/,
rts = /([?&])_=[^&]*/,
rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg,
// #7653, #8125, #8152: local protocol detection
rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
rnoContent = /^(?:GET|HEAD)$/,
rprotocol = /^\/\//,
rurl = /^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/, // Keep a copy of the old load method
_load = jQuery.fn.load, /* Prefilters
* 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
* 2) These are called:
* - BEFORE asking for a transport
* - AFTER param serialization (s.data is a string if s.processData is true)
* 3) key is the dataType
* 4) the catchall symbol "*" can be used
* 5) execution will start with transport dataType and THEN continue down to "*" if needed
*/
//预过滤器,在发送ajax之前做一些处理
prefilters = {}, /* Transports bindings
* 1) key is the dataType
* 2) the catchall symbol "*" can be used
* 3) selection will start with transport dataType and THEN go to "*" if needed
*/
//分发器,
transports = {}, // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
allTypes = "*/".concat("*"); // #8138, IE may throw an exception when accessing
// a field from window.location if document.domain has been set
try {
ajaxLocation = location.href;
} catch( e ) {
// 兼容性,动态创建a标签,a标签的href是空,然后就可以获取当前页面地址
ajaxLocation = document.createElement( "a" );
ajaxLocation.href = "";
ajaxLocation = ajaxLocation.href;
} // Segment location into parts
ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || []; // Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
function addToPrefiltersOrTransports( structure ) { // dataTypeExpression is optional and defaults to "*"
return function( dataTypeExpression, func ) { if ( typeof dataTypeExpression !== "string" ) {
func = dataTypeExpression;
dataTypeExpression = "*";
} var dataType,
i = 0,
dataTypes = dataTypeExpression.toLowerCase().match( core_rnotwhite ) || []; if ( jQuery.isFunction( func ) ) {
// For each dataType in the dataTypeExpression
while ( (dataType = dataTypes[i++]) ) {
// Prepend if requested
if ( dataType[0] === "+" ) {
dataType = dataType.slice( 1 ) || "*";
(structure[ dataType ] = structure[ dataType ] || []).unshift( func ); // Otherwise append
} else {
(structure[ dataType ] = structure[ dataType ] || []).push( func );
}
}
}
};
} // Base inspection function for prefilters and transports
function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { var inspected = {},
seekingTransport = ( structure === transports ); function inspect( dataType ) {
var selected;
inspected[ dataType ] = true;
jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
if( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
options.dataTypes.unshift( dataTypeOrTransport );
inspect( dataTypeOrTransport );
return false;
} else if ( seekingTransport ) {
return !( selected = dataTypeOrTransport );
}
});
return selected;
} return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
} // 继承
function ajaxExtend( target, src ) {
var key, deep,
flatOptions = jQuery.ajaxSettings.flatOptions || {}; for ( key in src ) {
if ( src[ key ] !== undefined ) {
( flatOptions[ key ] ? target : ( deep || (deep = {}) ) )[ key ] = src[ key ];
}
}
if ( deep ) {
jQuery.extend( true, target, deep );
} return target;
} // $('#div1').load("1.html ol,{'name':'hello'}",function(a,b,c){})
jQuery.fn.load = function( url, params, callback ) {
if ( typeof url !== "string" && _load ) {
return _load.apply( this, arguments );
} var selector, type, response,
self = this,
off = url.indexOf(" "); if ( off >= 0 ) {
selector = url.slice( off );
url = url.slice( 0, off );
} // If it's a function
if ( jQuery.isFunction( params ) ) { // We assume that it's the callback
callback = params;
params = undefined; // Otherwise, build a param string
} else if ( params && typeof params === "object" ) {
type = "POST";//对象是post方式
} // If we have elements to modify, make the request
if ( self.length > 0 ) {
jQuery.ajax({
url: url,
type: type,//默认是get
dataType: "html",//返回数据类型
data: params //传输数据
}).done(function( responseText ) {//responseText返回的数据
response = arguments;
//self.html()添加到本元素的html内容中
self.html( selector ? //有过滤器 //动态创建div,添加返回的html,查找过滤器
jQuery("<div>").append( jQuery.parseHTML( responseText ) ).find( selector ) : // Otherwise use the full result
responseText );
//complete是完成不管成功失败
}).complete( callback && function( jqXHR, status ) {
self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );
});
} return this;
}; // Attach a bunch of functions for handling common AJAX events
jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ){
jQuery.fn[ type ] = function( fn ){
return this.on( type, fn );
};
}); jQuery.extend({ // Counter for holding the number of active queries
active: 0, // Last-Modified header cache for next request
lastModified: {},
etag: {}, //ajax默认参数
ajaxSettings: {
url: ajaxLocation,//ajaxLocation = location.href;默认是当前页面地址
type: "GET",
isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ), //是否本地
global: true, //是否能触发全局事件参数
processData: true,
async: true,
contentType: "application/x-www-form-urlencoded; charset=UTF-8",//数据编码操作, //注释是一样的,注释不写就是undefined,
/*
timeout: 0, //超时处理,超时就提示是否继续等待还是离开还是刷新
data: null,
dataType: null,
username: null, //服务器的验证
password: null,
cache: null, //cache:false,请求后面加一个时间戳,不会有缓存
throws: false, //
traditional: false, //传统模式
headers: {},
*/ accepts: {
"*": allTypes,
text: "text/plain",
html: "text/html",
xml: "application/xml, text/xml",
json: "application/json, text/javascript"
}, //检测响应头信息,判断返回的类型。
contents: {
xml: /xml/,
html: /html/,
json: /json/
}, responseFields: {
xml: "responseXML",
text: "responseText",
json: "responseJSON"
}, // 根据返回的响应头信息,做不同的转换。
converters: { // Convert anything to text
"* text": String, // Text to html (true = no transformation)
"text html": true, // Evaluate text as a json expression
"text json": jQuery.parseJSON, // Parse text as xml
"text xml": jQuery.parseXML
}, // For options that shouldn't be deep extended:
// you can add your own custom options here if
// and when you create one that shouldn't be
// deep extended (see ajaxExtend)
flatOptions: {
url: true,
context: true
}
}, // 对json继承的封装
ajaxSetup: function( target, settings ) {
return settings ? //继承json过去
ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : // Extending ajaxSettings
ajaxExtend( jQuery.ajaxSettings, target );
}, ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
ajaxTransport: addToPrefiltersOrTransports( transports ), // Main method
/*
$.ajax({
url:url,
type:'post',
success : function(){},
error:function(){}
}); $.ajax('url',{
type:'post',
success : function(){},
error:function(){}
});
*/
ajax: function( url, options ) {//url是地址,options是json
if ( typeof url === "object" ) {//url是json就赋值给options
options = url;
url = undefined;
} // Force options to be an object
options = options || {}; var transport,
// URL without anti-cache param
cacheURL,
// Response headers
responseHeadersString,
responseHeaders,
// timeout handle
timeoutTimer,
// Cross-domain detection vars
parts,
// To know if global events are to be dispatched
fireGlobals,
// Loop variable
i,
//options覆盖默认参数,继承
s = jQuery.ajaxSetup( {}, options ),
// Callbacks context
callbackContext = s.context || s,
// Context for global events is callbackContext if it is a DOM node or jQuery collection
globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
jQuery( callbackContext ) :
jQuery.event,
// 延迟对象,回调对象
deferred = jQuery.Deferred(),
completeDeferred = jQuery.Callbacks("once memory"),
// Status-dependent callbacks
statusCode = s.statusCode || {},
// Headers (they are sent all at once)
requestHeaders = {},
requestHeadersNames = {},
// The jqXHR state
state = 0,
// Default abort message
strAbort = "canceled",
// Fake xhr
jqXHR = {
readyState: 0,//0--4,4的时候就是完成了 // 获取响应头
getResponseHeader: function( key ) {
var match;
if ( state === 2 ) {
if ( !responseHeaders ) {
responseHeaders = {};
while ( (match = rheaders.exec( responseHeadersString )) ) {
responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
}
}
match = responseHeaders[ key.toLowerCase() ];
}
return match == null ? null : match;
}, // Raw string
getAllResponseHeaders: function() {
return state === 2 ? responseHeadersString : null;
}, // 设置请求头
setRequestHeader: function( name, value ) {
var lname = name.toLowerCase();
if ( !state ) {
name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
requestHeaders[ name ] = value;
}
return this;
}, // Overrides response content-type header
overrideMimeType: function( type ) {
if ( !state ) {
s.mimeType = type;
}
return this;
}, // Status-dependent callbacks
statusCode: function( map ) {
var code;
if ( map ) {
if ( state < 2 ) {
for ( code in map ) {
// Lazy-add the new callback in a way that preserves old ones
statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
}
} else {
// Execute the appropriate callbacks
jqXHR.always( map[ jqXHR.status ] );
}
}
return this;
}, // 错误的时候,例如超时,报错
abort: function( statusText ) {
var finalText = statusText || strAbort;
if ( transport ) {
transport.abort( finalText );
}
done( 0, finalText );
return this;
}
}; // 方法加进去
deferred.promise( jqXHR ).complete = completeDeferred.add;
jqXHR.success = jqXHR.done;
jqXHR.error = jqXHR.fail; // 对url处理,
s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" )
.replace( rprotocol, ajaxLocParts[ 1 ] + "//" ); // Alias method option to type as per ticket #12004
s.type = options.method || options.type || s.method || s.type; // Extract dataTypes list
s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( core_rnotwhite ) || [""]; // 跨域处理,不跟本网页是同一个主机地址。
if ( s.crossDomain == null ) {
parts = rurl.exec( s.url.toLowerCase() );
s.crossDomain = !!( parts &&
( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
( parts[ 3 ] || ( parts[ 1 ] === "http:" ? "80" : "443" ) ) !==
( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? "80" : "443" ) ) )
);
} // Convert data if not already a string
if ( s.data && s.processData && typeof s.data !== "string" ) {
s.data = jQuery.param( s.data, s.traditional );
} // 触发回调
inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); // 请求完成了
if ( state === 2 ) {
return jqXHR;
} // We can fire global events as of now if asked to
fireGlobals = s.global; // Watch for a new set of requests
if ( fireGlobals && jQuery.active++ === 0 ) {
jQuery.event.trigger("ajaxStart");
} // Uppercase the type
s.type = s.type.toUpperCase(); // Determine if request has content
s.hasContent = !rnoContent.test( s.type ); // Save the URL in case we're toying with the If-Modified-Since
// and/or If-None-Match header later on
cacheURL = s.url; // More options handling for requests with no content
if ( !s.hasContent ) { // If data is available, append data to url
if ( s.data ) {
cacheURL = ( s.url += ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
// #9682: remove data so that it's not used in an eventual retry
delete s.data;
} // 不缓存就添加随机数
if ( s.cache === false ) {
s.url = rts.test( cacheURL ) ? // If there is already a '_' parameter, set its value
cacheURL.replace( rts, "$1_=" + ajax_nonce++ ) : // Otherwise add one to the end
cacheURL + ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ajax_nonce++;
}
} // 设置头信息,数据没有改变走缓存,通过后端配合,数据改变发起新的请求。
if ( s.ifModified ) {
if ( jQuery.lastModified[ cacheURL ] ) {
jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
}
if ( jQuery.etag[ cacheURL ] ) {
jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
}
} // Set the correct header, if data is being sent
if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
jqXHR.setRequestHeader( "Content-Type", s.contentType );
} // 设置请求类型
jqXHR.setRequestHeader(
"Accept",
s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
s.accepts[ "*" ]
); // Check for headers option
for ( i in s.headers ) {
jqXHR.setRequestHeader( i, s.headers[ i ] );
} // Allow custom headers/mimetypes and early abort
if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
// Abort if not done already and return
return jqXHR.abort();
} // aborting is no longer a cancellation
strAbort = "abort"; // Install callbacks on deferreds
for ( i in { success: 1, error: 1, complete: 1 } ) {
jqXHR[ i ]( s[ i ] );
} // Get transport
transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); // If no transport, we auto-abort
if ( !transport ) {
done( -1, "No Transport" );
} else {
jqXHR.readyState = 1; // Send global event
if ( fireGlobals ) {
globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
}
// Timeout
if ( s.async && s.timeout > 0 ) {
timeoutTimer = setTimeout(function() {
jqXHR.abort("timeout");
}, s.timeout );
} try {
state = 1;
transport.send( requestHeaders, done );
} catch ( e ) {
// Propagate exception as error if not done
if ( state < 2 ) {
done( -1, e );
// Simply rethrow otherwise
} else {
throw e;
}
}
} // Callback for when everything is done
function done( status, nativeStatusText, responses, headers ) {
var isSuccess, success, error, response, modified,
statusText = nativeStatusText; // Called once
if ( state === 2 ) {
return;
} // State is "done" now
state = 2; // Clear timeout if it exists
if ( timeoutTimer ) {
clearTimeout( timeoutTimer );
} // Dereference transport for early garbage collection
// (no matter how long the jqXHR object will be used)
transport = undefined; // Cache response headers
responseHeadersString = headers || ""; // Set readyState
jqXHR.readyState = status > 0 ? 4 : 0; // Determine if successful
isSuccess = status >= 200 && status < 300 || status === 304; // Get response data
if ( responses ) {
response = ajaxHandleResponses( s, jqXHR, responses );
} // Convert no matter what (that way responseXXX fields are always set)
response = ajaxConvert( s, response, jqXHR, isSuccess ); // If successful, handle type chaining
if ( isSuccess ) { // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
if ( s.ifModified ) {
modified = jqXHR.getResponseHeader("Last-Modified");
if ( modified ) {
jQuery.lastModified[ cacheURL ] = modified;
}
modified = jqXHR.getResponseHeader("etag");
if ( modified ) {
jQuery.etag[ cacheURL ] = modified;
}
} // if no content
if ( status === 204 || s.type === "HEAD" ) {
statusText = "nocontent"; // if not modified
} else if ( status === 304 ) {
statusText = "notmodified"; // If we have data, let's convert it
} else {
statusText = response.state;
success = response.data;
error = response.error;
isSuccess = !error;
}
} else {
// We extract error from statusText
// then normalize statusText and status for non-aborts
error = statusText;
if ( status || !statusText ) {
statusText = "error";
if ( status < 0 ) {
status = 0;
}
}
} // Set data for the fake xhr object
jqXHR.status = status;
jqXHR.statusText = ( nativeStatusText || statusText ) + ""; // Success/Error
if ( isSuccess ) {
deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
} else {
deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
} // Status-dependent callbacks
jqXHR.statusCode( statusCode );
statusCode = undefined; if ( fireGlobals ) {
globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
[ jqXHR, s, isSuccess ? success : error ] );
} // Complete
completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); if ( fireGlobals ) {
globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
// Handle the global AJAX counter
if ( !( --jQuery.active ) ) {
jQuery.event.trigger("ajaxStop");
}
}
}
//ajax方法结束后,返回jqXHR对象,$.ajax()方法后面就可以调用done、fail方法,$.ajax({url:url}).done().fail();
return jqXHR;
}, getJSON: function( url, data, callback ) {
return jQuery.get( url, data, callback, "json" );//调用jQuery.ajax()
}, getScript: function( url, callback ) {//请求js不传递参数
return jQuery.get( url, undefined, callback, "script" );
//调用jQuery.ajax()
}
}); //["get","post"]每一个元素调用function( i, method ),i是索引method是'get'、'post'
jQuery.each( [ "get", "post" ], function( i, method ) {
//get,post挂载到jQuery静态方法,通过中括号挂载。
//$.get('url',{'aa':123},function(){'成功回调'},'返回数据类型xml、json')
jQuery[ method ] = function( url, data, callback, type ) {
// shift arguments if data argument was omitted
if ( jQuery.isFunction( data ) ) {
type = type || callback;
callback = data;
data = undefined;
} return jQuery.ajax({
url: url,
type: method,
dataType: type,
data: data,
success: callback
});
};
}); /* Handles responses to an ajax request:
* - finds the right dataType (mediates between content-type and expected dataType)
* - returns the corresponding response
*/
function ajaxHandleResponses( s, jqXHR, responses ) { var ct, type, finalDataType, firstDataType,
contents = s.contents,
dataTypes = s.dataTypes; // Remove auto dataType and get content-type in the process
while( dataTypes[ 0 ] === "*" ) {
dataTypes.shift();
if ( ct === undefined ) {
ct = s.mimeType || jqXHR.getResponseHeader("Content-Type");
}
} // Check if we're dealing with a known content-type
if ( ct ) {
for ( type in contents ) {
if ( contents[ type ] && contents[ type ].test( ct ) ) {
dataTypes.unshift( type );
break;
}
}
} // Check to see if we have a response for the expected dataType
if ( dataTypes[ 0 ] in responses ) {
finalDataType = dataTypes[ 0 ];
} else {
// Try convertible dataTypes
for ( type in responses ) {
if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
finalDataType = type;
break;
}
if ( !firstDataType ) {
firstDataType = type;
}
}
// Or just use first one
finalDataType = finalDataType || firstDataType;
} // If we found a dataType
// We add the dataType to the list if needed
// and return the corresponding response
if ( finalDataType ) {
if ( finalDataType !== dataTypes[ 0 ] ) {
dataTypes.unshift( finalDataType );
}
return responses[ finalDataType ];
}
} //类型转换器,匹配返回的数据类型
function ajaxConvert( s, response, jqXHR, isSuccess ) {
var conv2, current, conv, tmp, prev,
converters = {},
// Work with a copy of dataTypes in case we need to modify it for conversion
dataTypes = s.dataTypes.slice(); // Create converters map with lowercased keys
if ( dataTypes[ 1 ] ) {
for ( conv in s.converters ) {
converters[ conv.toLowerCase() ] = s.converters[ conv ];
}
} current = dataTypes.shift(); // Convert to each sequential dataType
while ( current ) { if ( s.responseFields[ current ] ) {
jqXHR[ s.responseFields[ current ] ] = response;
} // Apply the dataFilter if provided
if ( !prev && isSuccess && s.dataFilter ) {
response = s.dataFilter( response, s.dataType );
} prev = current;
current = dataTypes.shift(); if ( current ) { // There's only work to do if current dataType is non-auto
if ( current === "*" ) { current = prev; // Convert response if prev dataType is non-auto and differs from current
} else if ( prev !== "*" && prev !== current ) { // Seek a direct converter
conv = converters[ prev + " " + current ] || converters[ "* " + current ]; // If none found, seek a pair
if ( !conv ) {
for ( conv2 in converters ) { // If conv2 outputs current
tmp = conv2.split( " " );
if ( tmp[ 1 ] === current ) { // If prev can be converted to accepted input
conv = converters[ prev + " " + tmp[ 0 ] ] ||
converters[ "* " + tmp[ 0 ] ];
if ( conv ) {
// Condense equivalence converters
if ( conv === true ) {
conv = converters[ conv2 ]; // Otherwise, insert the intermediate dataType
} else if ( converters[ conv2 ] !== true ) {
current = tmp[ 0 ];
dataTypes.unshift( tmp[ 1 ] );
}
break;
}
}
}
} // Apply converter (if not an equivalence)
if ( conv !== true ) { // Unless errors are allowed to bubble, catch and return them
if ( conv && s[ "throws" ] ) {
response = conv( response );
} else {
try {
response = conv( response );
} catch ( e ) {
return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current };
}
}
}
}
}
} return { state: "success", data: response };
}
// Install script dataType
jQuery.ajaxSetup({
accepts: {
script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
},
contents: {
script: /(?:java|ecma)script/
},
converters: {
"text script": function( text ) {
jQuery.globalEval( text );
return text;
}
}
}); // 发送ajax之前对数据做处理
jQuery.ajaxPrefilter( "script", function( s ) {
if ( s.cache === undefined ) {
s.cache = false;
}
if ( s.crossDomain ) {
s.type = "GET";
}
}); // Bind script tag hack transport
jQuery.ajaxTransport( "script", function( s ) {
// This transport only deals with cross domain requests
if ( s.crossDomain ) {
var script, callback;
return {
send: function( _, complete ) {
script = jQuery("<script>").prop({
async: true,
charset: s.scriptCharset,
src: s.url
}).on(
"load error",
callback = function( evt ) {
script.remove();
callback = null;
if ( evt ) {
complete( evt.type === "error" ? 404 : 200, evt.type );
}
}
);
document.head.appendChild( script[ 0 ] );
},
abort: function() {
if ( callback ) {
callback();
}
}
};
}
});
var oldCallbacks = [],
rjsonp = /(=)\?(?=&|$)|\?\?/; // Default jsonp settings
jQuery.ajaxSetup({
jsonp: "callback",
jsonpCallback: function() {
var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( ajax_nonce++ ) );
this[ callback ] = true;
return callback;
}
}); // Detect, normalize options and install callbacks for jsonp requests
jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { var callbackName, overwritten, responseContainer,
jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
"url" :
typeof s.data === "string" && !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && rjsonp.test( s.data ) && "data"
); // Handle iff the expected data type is "jsonp" or we have a parameter to set
if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) { // Get callback name, remembering preexisting value associated with it
callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
s.jsonpCallback() :
s.jsonpCallback; // Insert callback into url or form data
if ( jsonProp ) {
s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
} else if ( s.jsonp !== false ) {
s.url += ( ajax_rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
} // Use data converter to retrieve json after script execution
s.converters["script json"] = function() {
if ( !responseContainer ) {
jQuery.error( callbackName + " was not called" );
}
return responseContainer[ 0 ];
}; // force json dataType
s.dataTypes[ 0 ] = "json"; // Install callback
overwritten = window[ callbackName ];
window[ callbackName ] = function() {
responseContainer = arguments;
}; // Clean-up function (fires after converters)
jqXHR.always(function() {
// Restore preexisting value
window[ callbackName ] = overwritten; // Save back as free
if ( s[ callbackName ] ) {
// make sure that re-using the options doesn't screw things around
s.jsonpCallback = originalSettings.jsonpCallback; // save the callback name for future use
oldCallbacks.push( callbackName );
} // Call if it was a function and we have a response
if ( responseContainer && jQuery.isFunction( overwritten ) ) {
overwritten( responseContainer[ 0 ] );
} responseContainer = overwritten = undefined;
}); // Delegate to script
return "script";
}
});
jQuery.ajaxSettings.xhr = function() {
try {
return new XMLHttpRequest();
} catch( e ) {}
}; var xhrSupported = jQuery.ajaxSettings.xhr(),
xhrSuccessStatus = {
// file protocol always yields status code 0, assume 200
0: 200,
// Support: IE9
// #1450: sometimes IE returns 1223 when it should be 204
1223: 204
},
// Support: IE9
// We need to keep track of outbound xhr and abort them manually
// because IE is not smart enough to do it all by itself
xhrId = 0,
xhrCallbacks = {}; if ( window.ActiveXObject ) {
jQuery( window ).on( "unload", function() {
for( var key in xhrCallbacks ) {
xhrCallbacks[ key ]();
}
xhrCallbacks = undefined;
});
} jQuery.support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
jQuery.support.ajax = xhrSupported = !!xhrSupported; jQuery.ajaxTransport(function( options ) {
var callback;
// Cross domain only allowed if supported through XMLHttpRequest
if ( jQuery.support.cors || xhrSupported && !options.crossDomain ) {
return {
send: function( headers, complete ) {
var i, id,
//xhr是原生XMLHttpRequest对象
xhr = options.xhr();
xhr.open( options.type, options.url, options.async, options.username, options.password );
// Apply custom fields if provided
if ( options.xhrFields ) {
for ( i in options.xhrFields ) {
xhr[ i ] = options.xhrFields[ i ];
}
}
// 跨域处理
if ( options.mimeType && xhr.overrideMimeType ) {
xhr.overrideMimeType( options.mimeType );
}
// X-Requested-With header
// For cross-domain requests, seeing as conditions for a preflight are
// akin to a jigsaw puzzle, we simply never set it to be sure.
// (it can always be set on a per-request basis or even using ajaxSetup)
// For same-domain requests, won't change header if already provided.
if ( !options.crossDomain && !headers["X-Requested-With"] ) {
headers["X-Requested-With"] = "XMLHttpRequest";
}
// Set headers
for ( i in headers ) {
xhr.setRequestHeader( i, headers[ i ] );
}
// Callback
callback = function( type ) {
return function() {
if ( callback ) {
delete xhrCallbacks[ id ];
callback = xhr.onload = xhr.onerror = null;
if ( type === "abort" ) {
xhr.abort();
} else if ( type === "error" ) {
complete(
// file protocol always yields status 0, assume 404
xhr.status || 404,
xhr.statusText
);
} else {
complete(
xhrSuccessStatus[ xhr.status ] || xhr.status,
xhr.statusText,
// Support: IE9
// #11426: When requesting binary data, IE9 will throw an exception
// on any attempt to access responseText
typeof xhr.responseText === "string" ? {
text: xhr.responseText
} : undefined,
xhr.getAllResponseHeaders()
);
}
}
};
};
// 真正完成才会走onload
xhr.onload = callback();
xhr.onerror = callback("error");
// Create the abort callback
callback = xhrCallbacks[( id = xhrId++ )] = callback("abort");
// Do send the request
// This may raise an exception which is actually
// handled in jQuery.ajax (so no try/catch here)
//发送
xhr.send( options.hasContent && options.data || null );
},
abort: function() {
if ( callback ) {
callback();
}
}
};
}
}); }
//////////////////////////ajax结束//////////////////////////////////////////////////////////////////// //////////////////////////animate() : 运动的方法开始
{
var fxNow, timerId,
rfxtypes = /^(?:toggle|show|hide)$/,
rfxnum = new RegExp( "^(?:([+-])=|)(" + core_pnum + ")([a-z%]*)$", "i" ),
rrun = /queueHooks$/,
animationPrefilters = [ defaultPrefilter ],
tweeners = {
"*": [function( prop, value ) {
var tween = this.createTween( prop, value ),
target = tween.cur(),
parts = rfxnum.exec( value ),
unit = parts && parts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), // Starting value computation is required for potential unit mismatches
start = ( jQuery.cssNumber[ prop ] || unit !== "px" && +target ) &&
rfxnum.exec( jQuery.css( tween.elem, prop ) ),
scale = 1,
maxIterations = 20; if ( start && start[ 3 ] !== unit ) {
// Trust units reported by jQuery.css
unit = unit || start[ 3 ]; // Make sure we update the tween properties later on
parts = parts || []; // Iteratively approximate from a nonzero starting point
start = +target || 1; do {
// If previous iteration zeroed out, double until we get *something*
// Use a string for doubling factor so we don't accidentally see scale as unchanged below
scale = scale || ".5"; // Adjust and apply
start = start / scale;
jQuery.style( tween.elem, prop, start + unit ); // Update scale, tolerating zero or NaN from tween.cur()
// And breaking the loop if scale is unchanged or perfect, or if we've just had enough
} while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );
} // Update tween properties
if ( parts ) {
start = tween.start = +start || +target || 0;
tween.unit = unit;
// If a +=/-= token was provided, we're doing a relative animation
tween.end = parts[ 1 ] ?
start + ( parts[ 1 ] + 1 ) * parts[ 2 ] :
+parts[ 2 ];
} return tween;
}]
}; // Animations created synchronously will run synchronously
function createFxNow() {
setTimeout(function() {
fxNow = undefined;
});
return ( fxNow = jQuery.now() );
} function createTween( value, prop, animation ) {
var tween,
collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
index = 0,
length = collection.length;
for ( ; index < length; index++ ) {
if ( (tween = collection[ index ].call( animation, prop, value )) ) { // we're done with this property
return tween;
}
}
} function Animation( elem, properties, options ) {
var result,
stopped,
index = 0,
length = animationPrefilters.length,
deferred = jQuery.Deferred().always( function() {
// don't match elem in the :animated selector
delete tick.elem;
}),
tick = function() {
if ( stopped ) {
return false;
}
var currentTime = fxNow || createFxNow(),
remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
// archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497)
temp = remaining / animation.duration || 0,
percent = 1 - temp,
index = 0,
length = animation.tweens.length; for ( ; index < length ; index++ ) {
animation.tweens[ index ].run( percent );
} deferred.notifyWith( elem, [ animation, percent, remaining ]); if ( percent < 1 && length ) {
return remaining;
} else {
deferred.resolveWith( elem, [ animation ] );
return false;
}
},
animation = deferred.promise({
elem: elem,
props: jQuery.extend( {}, properties ),
opts: jQuery.extend( true, { specialEasing: {} }, options ),
originalProperties: properties,
originalOptions: options,
startTime: fxNow || createFxNow(),
duration: options.duration,
tweens: [],
createTween: function( prop, end ) {
var tween = jQuery.Tween( elem, animation.opts, prop, end,
animation.opts.specialEasing[ prop ] || animation.opts.easing );
animation.tweens.push( tween );
return tween;
},
stop: function( gotoEnd ) {
var index = 0,
// if we are going to the end, we want to run all the tweens
// otherwise we skip this part
length = gotoEnd ? animation.tweens.length : 0;
if ( stopped ) {
return this;
}
stopped = true;
for ( ; index < length ; index++ ) {
animation.tweens[ index ].run( 1 );
} // resolve when we played the last frame
// otherwise, reject
if ( gotoEnd ) {
deferred.resolveWith( elem, [ animation, gotoEnd ] );
} else {
deferred.rejectWith( elem, [ animation, gotoEnd ] );
}
return this;
}
}),
props = animation.props; propFilter( props, animation.opts.specialEasing ); for ( ; index < length ; index++ ) {
result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );
if ( result ) {
return result;
}
} jQuery.map( props, createTween, animation ); if ( jQuery.isFunction( animation.opts.start ) ) {
animation.opts.start.call( elem, animation );
} jQuery.fx.timer(
jQuery.extend( tick, {
elem: elem,
anim: animation,
queue: animation.opts.queue
})
); // attach callbacks from options
return animation.progress( animation.opts.progress )
.done( animation.opts.done, animation.opts.complete )
.fail( animation.opts.fail )
.always( animation.opts.always );
} //属性的过滤操作
function propFilter( props, specialEasing ) {
var index, name, easing, value, hooks; // camelCase, specialEasing and expand cssHook pass
for ( index in props ) {
name = jQuery.camelCase( index );
easing = specialEasing[ name ];
value = props[ index ];
if ( jQuery.isArray( value ) ) {
easing = value[ 1 ];
value = props[ index ] = value[ 0 ];
} if ( index !== name ) {
props[ name ] = value;
delete props[ index ];
} hooks = jQuery.cssHooks[ name ];
if ( hooks && "expand" in hooks ) {
value = hooks.expand( value );
delete props[ name ]; // not quite $.extend, this wont overwrite keys already present.
// also - reusing 'index' from above because we have the correct "name"
for ( index in value ) {
if ( !( index in props ) ) {
props[ index ] = value[ index ];
specialEasing[ index ] = easing;
}
}
} else {
specialEasing[ name ] = easing;
}
}
} jQuery.Animation = jQuery.extend( Animation, { tweener: function( props, callback ) {
if ( jQuery.isFunction( props ) ) {
callback = props;
props = [ "*" ];
} else {
props = props.split(" ");
} var prop,
index = 0,
length = props.length; for ( ; index < length ; index++ ) {
prop = props[ index ];
tweeners[ prop ] = tweeners[ prop ] || [];
tweeners[ prop ].unshift( callback );
}
}, prefilter: function( callback, prepend ) {
if ( prepend ) {
animationPrefilters.unshift( callback );
} else {
animationPrefilters.push( callback );
}
}
}); function defaultPrefilter( elem, props, opts ) {
/* jshint validthis: true */
var prop, value, toggle, tween, hooks, oldfire,
anim = this,
orig = {},
style = elem.style,
hidden = elem.nodeType && isHidden( elem ),
dataShow = data_priv.get( elem, "fxshow" ); // handle queue: false promises
if ( !opts.queue ) {
hooks = jQuery._queueHooks( elem, "fx" );
if ( hooks.unqueued == null ) {
hooks.unqueued = 0;
oldfire = hooks.empty.fire;
hooks.empty.fire = function() {
if ( !hooks.unqueued ) {
oldfire();
}
};
}
hooks.unqueued++; anim.always(function() {
// doing this makes sure that the complete handler will be called
// before this completes
anim.always(function() {
hooks.unqueued--;
if ( !jQuery.queue( elem, "fx" ).length ) {
hooks.empty.fire();
}
});
});
} // height/width overflow pass
if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
// Make sure that nothing sneaks out
// Record all 3 overflow attributes because IE9-10 do not
// change the overflow attribute when overflowX and
// overflowY are set to the same value
opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; // Set display property to inline-block for height/width
// animations on inline elements that are having width/height animated
if ( jQuery.css( elem, "display" ) === "inline" &&
jQuery.css( elem, "float" ) === "none" ) { style.display = "inline-block";
}
} if ( opts.overflow ) {
style.overflow = "hidden";
anim.always(function() {
style.overflow = opts.overflow[ 0 ];
style.overflowX = opts.overflow[ 1 ];
style.overflowY = opts.overflow[ 2 ];
});
} // show/hide pass
for ( prop in props ) {
value = props[ prop ];
if ( rfxtypes.exec( value ) ) {
delete props[ prop ];
toggle = toggle || value === "toggle";
if ( value === ( hidden ? "hide" : "show" ) ) { // If there is dataShow left over from a stopped hide or show and we are going to proceed with show, we should pretend to be hidden
if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
hidden = true;
} else {
continue;
}
}
orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
}
} if ( !jQuery.isEmptyObject( orig ) ) {
if ( dataShow ) {
if ( "hidden" in dataShow ) {
hidden = dataShow.hidden;
}
} else {
dataShow = data_priv.access( elem, "fxshow", {} );
} // store state if its toggle - enables .stop().toggle() to "reverse"
if ( toggle ) {
dataShow.hidden = !hidden;
}
if ( hidden ) {
jQuery( elem ).show();
} else {
anim.done(function() {
jQuery( elem ).hide();
});
}
anim.done(function() {
var prop; data_priv.remove( elem, "fxshow" );
for ( prop in orig ) {
jQuery.style( elem, prop, orig[ prop ] );
}
});
for ( prop in orig ) {
tween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); if ( !( prop in dataShow ) ) {
dataShow[ prop ] = tween.start;
if ( hidden ) {
tween.end = tween.start;
tween.start = prop === "width" || prop === "height" ? 1 : 0;
}
}
}
}
} function Tween( elem, options, prop, end, easing ) {
return new Tween.prototype.init( elem, options, prop, end, easing );
}
jQuery.Tween = Tween; Tween.prototype = {
constructor: Tween,
init: function( elem, options, prop, end, easing, unit ) {
this.elem = elem;
this.prop = prop;
this.easing = easing || "swing";
this.options = options;
this.start = this.now = this.cur();
this.end = end;
this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
},
cur: function() {
var hooks = Tween.propHooks[ this.prop ]; return hooks && hooks.get ?
hooks.get( this ) :
Tween.propHooks._default.get( this );
},
//执行运动算法
run: function( percent ) {
var eased,
hooks = Tween.propHooks[ this.prop ]; if ( this.options.duration ) {
this.pos = eased = jQuery.easing[ this.easing ](
percent, this.options.duration * percent, 0, 1, this.options.duration
);
} else {
this.pos = eased = percent;
}
this.now = ( this.end - this.start ) * eased + this.start; if ( this.options.step ) {
this.options.step.call( this.elem, this.now, this );
} if ( hooks && hooks.set ) {
hooks.set( this );
} else {
Tween.propHooks._default.set( this );
}
return this;
}
}; Tween.prototype.init.prototype = Tween.prototype; Tween.propHooks = {
_default: {
get: function( tween ) {
var result; if ( tween.elem[ tween.prop ] != null &&
(!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) {
return tween.elem[ tween.prop ];
} // passing an empty string as a 3rd parameter to .css will automatically
// attempt a parseFloat and fallback to a string if the parse fails
// so, simple values such as "10px" are parsed to Float.
// complex values such as "rotate(1rad)" are returned as is.
result = jQuery.css( tween.elem, tween.prop, "" );
// Empty strings, null, undefined and "auto" are converted to 0.
return !result || result === "auto" ? 0 : result;
},
set: function( tween ) {
// use step hook for back compat - use cssHook if its there - use .style if its
// available and use plain properties where available
if ( jQuery.fx.step[ tween.prop ] ) {
jQuery.fx.step[ tween.prop ]( tween );
} else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) {
jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
} else {
tween.elem[ tween.prop ] = tween.now;
}
}
}
}; // Support: IE9
// Panic based approach to setting things on disconnected nodes Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
set: function( tween ) {
if ( tween.elem.nodeType && tween.elem.parentNode ) {
tween.elem[ tween.prop ] = tween.now;
}
}
}; jQuery.each([ "toggle", "show", "hide" ], function( i, name ) {
var cssFn = jQuery.fn[ name ];//之前的方法
jQuery.fn[ name ] = function( speed, easing, callback ) {
return speed == null || typeof speed === "boolean" ?
cssFn.apply( this, arguments ) ://有参数调用原来的
this.animate( genFx( name, true ), speed, easing, callback );//没参数调用animate,genFx( name, true )返回一个json:{ width : 'hide' , height : 'hide' , opacity : 'hide' }
};
}); jQuery.fn.extend({
fadeTo: function( speed, to, easing, callback ) { // 如果是隐藏,设置透明度是0再显示,再做动画。
return this.filter( isHidden ).css( "opacity", 0 ).show() // animate to the value specified
.end().animate({ opacity: to }, speed, easing, callback );
}, /*$('#div1').animate({ height : 300 },1000);
//配置写运动
$('#div1').animate({ width : 400 },{
duration : 'slow', //速度慢速
easing : 'linear', //匀速
complete : function(){ //完成回调
alert(111);
}
});*/
animate: function( prop, speed, easing, callback ) {
var empty = jQuery.isEmptyObject( prop ),
optall = jQuery.speed( speed, easing, callback ),//参数做配置
doAnimation = function() {
// Operate on a copy of prop so per-property easing won't be lost
var anim = Animation( this, jQuery.extend( {}, prop ), optall ); // Empty animations, or finishing resolves immediately
if ( empty || data_priv.get( this, "finish" ) ) {
anim.stop( true );
}
};
doAnimation.finish = doAnimation; return empty || optall.queue === false ?
this.each( doAnimation ) ://遍历函数
this.queue( optall.queue, doAnimation );//入队,doAnimation入队的每一个函数
},
stop: function( type, clearQueue, gotoEnd ) {
var stopQueue = function( hooks ) {
var stop = hooks.stop;
delete hooks.stop;
stop( gotoEnd );
}; if ( typeof type !== "string" ) {
gotoEnd = clearQueue;
clearQueue = type;
type = undefined;
}
if ( clearQueue && type !== false ) {//true
this.queue( type || "fx", [] );//队列清空
} return this.each(function() {
var dequeue = true,
index = type != null && type + "queueHooks",
timers = jQuery.timers,
data = data_priv.get( this ); if ( index ) {
if ( data[ index ] && data[ index ].stop ) {
stopQueue( data[ index ] );
}
} else {
for ( index in data ) {
if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
stopQueue( data[ index ] );
}
}
} for ( index = timers.length; index--; ) {
if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
timers[ index ].anim.stop( gotoEnd );
dequeue = false;
timers.splice( index, 1 );
}
} // start the next in the queue if the last step wasn't forced
// timers currently will call their complete callbacks, which will dequeue
// but only if they were gotoEnd
if ( dequeue || !gotoEnd ) {
jQuery.dequeue( this, type );
}
});
},
finish: function( type ) {
if ( type !== false ) {
type = type || "fx";
}
return this.each(function() {
var index,
data = data_priv.get( this ),
queue = data[ type + "queue" ],
hooks = data[ type + "queueHooks" ],
timers = jQuery.timers,
length = queue ? queue.length : 0; // enable finishing flag on private data
data.finish = true; // empty the queue first
jQuery.queue( this, type, [] ); if ( hooks && hooks.stop ) {
hooks.stop.call( this, true );
} // look for any active animations, and finish them
for ( index = timers.length; index--; ) {
if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
timers[ index ].anim.stop( true );
timers.splice( index, 1 );
}
} // look for any animations in the old queue and finish them
for ( index = 0; index < length; index++ ) {
if ( queue[ index ] && queue[ index ].finish ) {
queue[ index ].finish.call( this );
}
} // turn off finishing flag
delete data.finish;
});
}
}); // 创建json:{ width : 'hide' , height : 'hide' , opacity : 'hide' }
function genFx( type, includeWidth ) {
var which,
attrs = { height: type },
i = 0; // if we include width, step value is 1 to do all cssExpand values,
// if we don't include width, step value is 2 to skip over Left and Right
includeWidth = includeWidth? 1 : 0;
//
for( ; i < 4 ; i += 2 - includeWidth ) {
which = cssExpand[ i ];
attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
} if ( includeWidth ) {
attrs.opacity = attrs.width = type;
} return attrs;
} // Generate shortcuts for custom animations
jQuery.each({
slideDown: genFx("show"),
slideUp: genFx("hide"),
slideToggle: genFx("toggle"),
fadeIn: { opacity: "show" },
fadeOut: { opacity: "hide" },
fadeToggle: { opacity: "toggle" }
}, function( name, props ) {
jQuery.fn[ name ] = function( speed, easing, callback ) {
return this.animate( props, speed, easing, callback );
};
}); jQuery.speed = function( speed, easing, fn ) {
var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
complete: fn || !fn && easing ||
jQuery.isFunction( speed ) && speed,
duration: speed,
easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
}; ////jQuery.fx.off = true; //关闭页面所有的运动,运动时间为0,
opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
/*jQuery.fx.speeds = {
slow: 600,
fast: 200,
// Default speed
_default: 400
};*/
opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default; // normalize opt.queue - true/undefined/null -> "fx"
if ( opt.queue == null || opt.queue === true ) {
opt.queue = "fx";
} // 自己写的complete
opt.old = opt.complete; opt.complete = function() {
if ( jQuery.isFunction( opt.old ) ) {
opt.old.call( this );
} if ( opt.queue ) {
jQuery.dequeue( this, opt.queue );
}
}; return opt;
}; jQuery.easing = {
linear: function( p ) {
return p;
},
swing: function( p ) {
return 0.5 - Math.cos( p*Math.PI ) / 2;
}
}; jQuery.timers = [];
jQuery.fx = Tween.prototype.init;
jQuery.fx.tick = function() {
var timer,
timers = jQuery.timers,
i = 0; fxNow = jQuery.now(); for ( ; i < timers.length; i++ ) {
timer = timers[ i ];
// Checks the timer has not already been removed
if ( !timer() && timers[ i ] === timer ) {
timers.splice( i--, 1 );
}
} if ( !timers.length ) {
jQuery.fx.stop();//停止运动
}
fxNow = undefined;
}; jQuery.fx.timer = function( timer ) {
if ( timer() && jQuery.timers.push( timer ) ) {
jQuery.fx.start();
}
}; jQuery.fx.interval = 13; jQuery.fx.start = function() {
if ( !timerId ) {
timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );
}
}; jQuery.fx.stop = function() {
clearInterval( timerId );
timerId = null;
}; jQuery.fx.speeds = {
slow: 600,
fast: 200,
// Default speed
_default: 400
};
}
//////////////////////////animate() : 运动的方法结束
// Back Compat <1.8 extension point
jQuery.fx.step = {}; if ( jQuery.expr && jQuery.expr.filters ) {
jQuery.expr.filters.animated = function( elem ) {
return jQuery.grep(jQuery.timers, function( fn ) {
return elem === fn.elem;
}).length;
};
}
jQuery.fn.offset = function( options ) {
if ( arguments.length ) {//设置
return options === undefined ?
this :
this.each(function( i ) {//每一个元素设置
jQuery.offset.setOffset( this, options, i );
});
}
//获取
var docElem, win,
elem = this[ 0 ],
box = { top: 0, left: 0 },
doc = elem && elem.ownerDocument;//document if ( !doc ) {
return;
} docElem = doc.documentElement;//html标签 // Make sure it's not a disconnected DOM node
if ( !jQuery.contains( docElem, elem ) ) {
return box;
} // getBoundingClientRect()得到一些属性值:ClientRect {top: 8, right: 58, bottom: 58, left: 8, width: 50…},是到可视区的不包含滚动区域,
if ( typeof elem.getBoundingClientRect !== core_strundefined ) {
box = elem.getBoundingClientRect();
}
win = getWindow( doc );//获取window
//pageYOffset是y轴的滚动距离,clientTop边框宽度,
return {
top: box.top + win.pageYOffset - docElem.clientTop,
left: box.left + win.pageXOffset - docElem.clientLeft
};
}; jQuery.offset = {
//设置offset
setOffset: function( elem, options, i ) {
var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
position = jQuery.css( elem, "position" ),//获取元素的position属性,
curElem = jQuery( elem ),
props = {}; // Set position first, in-case top/left are set even on static elem
if ( position === "static" ) {//没有定位就加一个相对定位
elem.style.position = "relative";
} curOffset = curElem.offset();
curCSSTop = jQuery.css( elem, "top" );
curCSSLeft = jQuery.css( elem, "left" );
calculatePosition = ( position === "absolute" || position === "fixed" ) && ( curCSSTop + curCSSLeft ).indexOf("auto") > -1; // Need to be able to calculate position if either top or left is auto and position is either absolute or fixed
if ( calculatePosition ) {
curPosition = curElem.position();
curTop = curPosition.top;
curLeft = curPosition.left; } else {
curTop = parseFloat( curCSSTop ) || 0;
curLeft = parseFloat( curCSSLeft ) || 0;
} if ( jQuery.isFunction( options ) ) {
options = options.call( elem, i, curOffset );
} if ( options.top != null ) {
props.top = ( options.top - curOffset.top ) + curTop;
}
if ( options.left != null ) {
props.left = ( options.left - curOffset.left ) + curLeft;
} if ( "using" in options ) {
options.using.call( elem, props ); } else {
curElem.css( props );
}
}
}; jQuery.fn.extend({ position: function() {//获取
if ( !this[ 0 ] ) {
return;
} var offsetParent, offset,
elem = this[ 0 ],
parentOffset = { top: 0, left: 0 }; // Fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is it's only offset parent
if ( jQuery.css( elem, "position" ) === "fixed" ) {
// We assume that getBoundingClientRect is available when computed position is fixed
offset = elem.getBoundingClientRect(); } else {
// 原生方法找父级
offsetParent = this.offsetParent(); // Get correct offsets
offset = this.offset();
if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
parentOffset = offsetParent.offset();
} // Add offsetParent borders
parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
} // 自己的top减去父级的top
return {
top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
};
}, offsetParent: function() {
return this.map(function() {
var offsetParent = this.offsetParent || docElem; while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position") === "static" ) ) {
offsetParent = offsetParent.offsetParent;
} return offsetParent || docElem;
});
}
}); // Create scrollLeft and scrollTop methods
jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) {
var top = "pageYOffset" === prop; jQuery.fn[ method ] = function( val ) {
return jQuery.access( this, function( elem, method, val ) {
var win = getWindow( elem ); if ( val === undefined ) {
return win ? win[ prop ] : elem[ method ];
} if ( win ) {
win.scrollTo(
!top ? val : window.pageXOffset,
top ? val : window.pageYOffset
); } else {
elem[ method ] = val;//原生的方法
}
}, method, val, arguments.length, null );
};
}); function getWindow( elem ) {
return jQuery.isWindow( elem ) ? elem : elem.nodeType === 9 && elem.defaultView;
}
// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) {
// margin is only for outerHeight, outerWidth
jQuery.fn[ funcName ] = function( margin, value ) {
var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" ); return jQuery.access( this, function( elem, type, value ) {
var doc;
//是window就得到可视区的宽高
if ( jQuery.isWindow( elem ) ) {
// As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
// isn't a whole lot we can do. See pull request at this URL for discussion:
// https://github.com/jquery/jquery/pull/764
return elem.document.documentElement[ "client" + name ];
} //是document就得到页面的宽高
if ( elem.nodeType === 9 ) {
doc = elem.documentElement; // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height],
// whichever is greatest
return Math.max(
elem.body[ "scroll" + name ], doc[ "scroll" + name ],
elem.body[ "offset" + name ], doc[ "offset" + name ],
doc[ "client" + name ]
);
} return value === undefined ?
jQuery.css( elem, type, extra ) ://css方式获取 jQuery.style( elem, type, value, extra );//style方式获取
}, type, chainable ? margin : undefined, chainable, null );
};
});
});
// Limit scope pollution from any deprecated API
// (function() { // The number of elements contained in the matched element set
jQuery.fn.size = function() {
return this.length;
}; jQuery.fn.andSelf = jQuery.fn.addBack; // })();
if ( typeof module === "object" && module && typeof module.exports === "object" ) {
// Expose jQuery as module.exports in loaders that implement the Node
// module pattern (including browserify). Do not create the global, since
// the user will be storing it themselves locally, and globals are frowned
// upon in the Node module world.
module.exports = jQuery;
} else {
// Register as a named AMD module, since jQuery can be concatenated with other
// files that may use define, but not via a proper concatenation script that
// understands anonymous AMD modules. A named AMD is safest and most robust
// way to register. Lowercase jquery is used because AMD module names are
// derived from file names, and jQuery is normally delivered in a lowercase
// file name. Do this after creating the global so that if an AMD module wants
// to call noConflict to hide this version of jQuery, it will work.
if ( typeof define === "function" && define.amd ) {
define( "jquery", [], function () { return jQuery; } );
}
} // If there is a window object, that at least has a document property,
// define jQuery and $ identifiers
if ( typeof window === "object" && typeof window.document === "object" ) {
window.jQuery = window.$ = jQuery;
} })( window );

  

jquery2.0.3 全部源码的更多相关文章

  1. Android7.0 Phone应用源码分析(二) phone来电流程分析

    接上篇博文:Android7.0 Phone应用源码分析(一) phone拨号流程分析 今天我们再来分析下Android7.0 的phone的来电流程 1.1TelephonyFramework 当有 ...

  2. Android7.0 Phone应用源码分析(一) phone拨号流程分析

    1.1 dialer拨号 拨号盘点击拨号DialpadFragment的onClick方法会被调用 public void onClick(View view) { int resId = view. ...

  3. robotlegs2.0框架实例源码带注释

    robotlegs2.0框架实例源码带注释 Robotlegs2的Starling扩展 有个老外写了robotleges2的starling扩展,地址是 https://github.com/brea ...

  4. Android7.0 Phone应用源码分析(三) phone拒接流程分析

    本文主要分析Android拒接电话的流程,下面先来看一下拒接电话流程时序图 步骤1:滑动按钮到拒接图标,会调用到AnswerFragment的onDecline方法 com.android.incal ...

  5. 购买的wemall 6.0商城系统源码分享

    使用方法 1.解压目录 2.cd wemall6 && npm i 3.配置config下的config.json 4.npm start 摒弃以往的开发框架thinkphp,使用no ...

  6. Android7.0 Phone应用源码分析(四) phone挂断流程分析

    电话挂断分为本地挂断和远程挂断,下面我们就针对这两种情况各做分析 先来看下本地挂断电话的时序图: 步骤1:点击通话界面的挂断按钮,会调用到CallCardPresenter的endCallClicke ...

  7. spring5.0.2.RELEASE源码环境构建

    Spring5 源码下载注意事项 首先你的JDK 需要升级到1.8 以上.Spring3.0 开始,Spring 源码采用github 托管,不再提供官网下载链接.大家可自行去github 网站下载, ...

  8. spring-boot-2.0.3启动源码篇二 - run方法(一)之SpringApplicationRunListener

    前言 Springboot启动源码系列还只写了一篇,已经过去一周,又到了每周一更的时间了(是不是很熟悉?),大家有没有很期待了?我会尽量保证启动源码系列每周一更,争取不让大家每周的期望落空.一周之中可 ...

  9. spring-boot-2.0.3启动源码篇 - 阶段总结

    前言 开心一刻 朋友喜欢去按摩,第一次推门进来的是一个学生美眉,感觉还不错:后来经常去,有时是护士,有时是空姐,有时候是教师.昨天晚上推门进去的是一个女警察,长得贼好看,身材也很好,朋友嗷的一声就扑上 ...

随机推荐

  1. 状态压缩dp初学__$Corn Fields$

    明天计划上是要刷状压,但是作为现在还不会状压的\(ruoruo\)来说是一件非常苦逼的事情,所以提前学了一下状压\(dp\). 鸣谢\(hmq\ juju\)的友情帮助 状态压缩动态规划 本博文的大体 ...

  2. BZOJ 3600 替罪羊树+线段树

    思路: 当然是抄的黄学长的题解啦 //By SiriusRen #include <cstdio> #include <algorithm> using namespace s ...

  3. Internet Explorer Developer Channel 自动化测试 IE 浏览器

    IE 原生 Web Driver 调用,通过简单配置,即可自动化测试 IE 浏览器(目前仅限 Internet Explorer Developer Channel 版本).做一些自动化的操作,都是很 ...

  4. js动态创建 select选择框

    document.body.onclick = function(){ if(document.getElementById('vselect') === null){ document.body.i ...

  5. Signal programming

    Signal programming is used in the same sense as dataflow programming, and is similar to event-driven ...

  6. MySql系列之单表查询

    单表查询的语法 SELECT 字段1,字段2... FROM 表名 WHERE 条件 GROUP BY field HAVING 筛选 ORDER BY field LIMIT 限制条数 关键字的执行 ...

  7. Win 7系统倒计时!

    3月25日消息,近日微软已经开始通知当前正在使用Windows 7的用户,该操作系统“接近尾声”.微软表示计划在2020年1月14日终止对Windows 7的所有支持.但结束Windows 7似乎并不 ...

  8. 题解 P2195 【HXY造公园】

    天哪这道题竟然只有一篇题解! emm,首先读题看完两个操作就已经有很明确的思路了,显然是并查集+树的直径 一波解决. 并查集不多说了,如果不了解的可以看这里. 树的直径的思路很朴实,就是两边DFS(B ...

  9. HBase 1.1.2 优化插入 Region预分配

    预分Region 与 不预分Region 的测试: 1 不预分Region:       23~29秒插入100W数据   并且蛋疼的是每次都写入一个 RegionServer 且  只在一个 Reg ...

  10. android 在短信发送界面, 短信发送失败时,提示音不完整,会被中断

    1. 当一条SMS到来, 此时SMS是unseen状态, 就会弹出Notification提示用户 2. 但假设处于同一个联系人的界面下, 用户会立马看到这条SMS, 此时这条SMS会被高速的标记为s ...