jQuery源码学习笔记五 六 七 八 转

jQuery源码学习笔记五 六 七 八 转

Js代码  

  1. <p>在正式深入jQuery的核心功能选择器之前,还有一些方法,基本都是数组方法,用于遴选更具体的需求,如获得某个元素的所有祖选元素啦,等等。接着是其缓存机制data。</p>
  2. <pre class="brush:javascript;gutter:false;toolbar:false">
  3. //@author  司徒正美|なさみ|cheng http://www.cnblogs.com/rubylouvre/  All rights reserved
  4. //去除两边的空白
  5. trim: function( text ) {
  6. return (text || "").replace( /^\s+|\s+$/g, "" );
  7. },
  8. //转换成数组,很大众的方法
  9. makeArray: function( array ) {
  10. var ret = [];
  11. if( array != null ){
  12. var i = array.length;
  13. // The window, strings (and functions) also have ‘length‘
  14. if( i == null || typeof array === "string" || jQuery.isFunction(array) || array.setInterval )
  15. ret[0] = array;//就只有一元素
  16. else
  17. while( i )//处理数组
  18. ret[--i] = array[i];
  19. }
  20. return ret;
  21. },
  22. //判断是否在数组中,类似indexOf
  23. inArray: function( elem, array ) {
  24. for ( var i = 0, length = array.length; i &lt; length; i++ )
  25. // Use === because on IE, window == document
  26. if ( array[ i ] === elem )
  27. return i;
  28. return -1;
  29. },
  30. //把新元素或第二个数组加入第一个数组中
  31. //类似数组的concat
  32. merge: function( first, second ) {
  33. // We have to loop this way because IE & Opera overwrite the length
  34. // expando of getElementsByTagName
  35. var i = 0, elem, pos = first.length;
  36. // Also, we need to make sure that the correct elements are being returned
  37. // (IE returns comment nodes in a ‘*‘ query)
  38. if ( !jQuery.support.getAll ) {
  39. while ( (elem = second[ i++ ]) != null )
  40. if ( elem.nodeType != 8 )
  41. first[ pos++ ] = elem;
  42. } else
  43. while ( (elem = second[ i++ ]) != null )
  44. first[ pos++ ] = elem;
  45. return first;
  46. },
  47. //过滤重复元素,用done这个普通对象做过滤器(因为键如果同名将被覆盖掉)
  48. unique: function( array ) {
  49. var ret = [], done = {};
  50. try {
  51. for ( var i = 0, length = array.length; i &lt; length; i++ ) {
  52. var id = jQuery.data( array[ i ] );
  53. if ( !done[ id ] ) {
  54. done[ id ] = true;
  55. ret.push( array[ i ] );
  56. }
  57. }
  58. } catch( e ) {
  59. ret = array;
  60. }
  61. return ret;
  62. },
  63. //类似数组的filter,这方法起得真不好,通常这都是与正则有关的……
  64. //$.grep( [0,1,2], function(n,i){
  65. //  return n &gt; 0;
  66. //});
  67. //[1, 2]
  68. grep: function( elems, callback, inv ) {
  69. var ret = [];
  70. // Go through the array, only saving the items
  71. // that pass the validator function
  72. //写法很特别,callback之前的!是为了防止回调函数没有返回值
  73. //javascript默认没有返回值的函数都返回undefined,这样一搞
  74. //就变成true,原来返回true的变成false,我们需要负负得正,中和一下
  75. //于是!=出场了,而inv也是未必存在的,用!强制转换成布尔
  76. for ( var i = 0, length = elems.length; i &lt; length; i++ )
  77. if ( !inv != !callback( elems[ i ], i ) )
  78. ret.push( elems[ i ] );
  79. return ret;
  80. },
  81. //就是数组中的map
  82. map: function( elems, callback ) {
  83. var ret = [];
  84. // Go through the array, translating each of the items to their
  85. // new value (or values).
  86. for ( var i = 0, length = elems.length; i &lt; length; i++ ) {
  87. var value = callback( elems[ i ], i );
  88. if ( value != null )
  89. ret[ ret.length ] = value;
  90. }
  91. return ret.concat.apply( [], ret );
  92. }
  93. });
  94. // jQuery.browser下面的方法已经被废弃了,这些都是为兼容以前的版本与插件用
  95. var userAgent = navigator.userAgent.toLowerCase();
  96. // Figure out what browser is being used
  97. jQuery.browser = {
  98. version: (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,‘0‘])[1],
  99. safari: /webkit/.test( userAgent ),
  100. opera: /opera/.test( userAgent ),
  101. msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
  102. mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent )
  103. };
  104. //把以下方法parent,parents,next……添加到jQuery的原型上去,都是一些过滤方法
  105. jQuery.each({
  106. parent: function(elem){return elem.parentNode;},
  107. parents: function(elem){return jQuery.dir(elem,"parentNode");},
  108. next: function(elem){return jQuery.nth(elem,2,"nextSibling");},
  109. prev: function(elem){return jQuery.nth(elem,2,"previousSibling");},
  110. nextAll: function(elem){return jQuery.dir(elem,"nextSibling");},
  111. prevAll: function(elem){return jQuery.dir(elem,"previousSibling");},
  112. siblings: function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},
  113. children: function(elem){return jQuery.sibling(elem.firstChild);},
  114. contents: function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}
  115. }, function(name, fn){
  116. jQuery.fn[ name ] = function( selector ) {//方法体
  117. var ret = jQuery.map( this, fn );
  118. if ( selector && typeof selector == "string" )
  119. ret = jQuery.multiFilter( selector, ret );
  120. return this.pushStack( jQuery.unique( ret ), name, selector );
  121. };
  122. });
  123. //把以下方法appendTo,prependTo,insertBefore……添加到jQuery的原型上去,
  124. //利用已有的append,prepend……方法构建
  125. jQuery.each({
  126. appendTo: "append",
  127. prependTo: "prepend",
  128. insertBefore: "before",
  129. insertAfter: "after",
  130. replaceAll: "replaceWith"
  131. }, function(name, original){
  132. jQuery.fn[ name ] = function( selector ) {
  133. var ret = [], insert = jQuery( selector );
  134. for ( var i = 0, l = insert.length; i &lt; l; i++ ) {
  135. var elems = (i &gt; 0 ? this.clone(true) : this).get();
  136. jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
  137. ret = ret.concat( elems );
  138. }
  139. return this.pushStack( ret, name, selector );
  140. };
  141. });
  142. //一些重要常用的静态方法
  143. jQuery.each({
  144. removeAttr: function( name ) {
  145. jQuery.attr( this, name, "" );
  146. if (this.nodeType == 1)
  147. this.removeAttribute( name );
  148. },
  149. addClass: function( classNames ) {
  150. jQuery.className.add( this, classNames );
  151. },
  152. removeClass: function( classNames ) {
  153. jQuery.className.remove( this, classNames );
  154. },
  155. toggleClass: function( classNames, state ) {
  156. if( typeof state !== "boolean" )
  157. state = !jQuery.className.has( this, classNames );
  158. jQuery.className[ state ? "add" : "remove" ]( this, classNames );
  159. },
  160. remove: function( selector ) {
  161. if ( !selector || jQuery.filter( selector, [ this ] ).length ) {
  162. // Prevent memory leaks
  163. jQuery( "*", this ).add([this]).each(function(){
  164. jQuery.event.remove(this);//★★★★★
  165. jQuery.removeData(this);
  166. });
  167. if (this.parentNode)
  168. this.parentNode.removeChild( this );
  169. }
  170. },
  171. empty: function() {
  172. // Remove element nodes and prevent memory leaks
  173. jQuery(this).children().remove();
  174. // Remove any remaining nodes
  175. while ( this.firstChild )
  176. this.removeChild( this.firstChild );
  177. }
  178. }, function(name, fn){
  179. jQuery.fn[ name ] = function(){
  180. return this.each( fn, arguments );
  181. };
  182. });
  183. //将带单位的数值去掉单位
  184. // Helper function used by the dimensions and offset modules
  185. function num(elem, prop) {
  186. return elem[0] && parseInt( jQuery.curCSS(elem[0], prop, true), 10 ) || 0;
  187. }
  188. </pre>
  189. <p>接着下来看jQuery的缓存机制,jQuery的性能很大部分依仗于它。</p>
  190. <pre class="brush:javascript;gutter:false;toolbar:false">
  191. //@author  司徒正美|RestlessDream|なさみ|cheng http://www.cnblogs.com/rubylouvre/  All rights reserved
  192. var expando = "jQuery" + now(), uuid = 0, windowData = {};
  193. jQuery.extend({
  194. cache: {},
  195. data: function( elem, name, data ) {
  196. //坚决不染指window
  197. elem = elem == window ?
  198. windowData :
  199. elem;
  200. //在elem上设置一个变量
  201. var id = elem[ expando ];
  202. // Compute a unique ID for the element
  203. if ( !id )
  204. //  同时为id,elem[expando]赋值,值为单一数字
  205. id = elem[ expando ] = ++uuid;
  206. // Only generate the data cache if we‘re
  207. // trying to access or manipulate it
  208. if ( name && !jQuery.cache[ id ] )
  209. //在jQuery.cache上开辟一个对象,专门用于储存与那个elem有关的东西
  210. jQuery.cache[ id ] = {};
  211. // Prevent overriding the named cache with undefined values
  212. if ( data !== undefined )//data必须定义
  213. jQuery.cache[ id ][ name ] = data;
  214. // Return the named cache data, or the ID for the element
  215. //根据第二个参数是否存在决定返回的是缓存数据还是element的特别ID
  216. return name ?
  217. jQuery.cache[ id ][ name ] :
  218. id;
  219. },
  220. //移除缓存数据
  221. removeData: function( elem, name ) {
  222. elem = elem == window ?
  223. windowData :
  224. elem;
  225. var id = elem[ expando ];
  226. // If we want to remove a specific section of the element‘s data
  227. if ( name ) {
  228. if ( jQuery.cache[ id ] ) {
  229. // Remove the section of cache data
  230. delete jQuery.cache[ id ][ name ];
  231. // If we‘ve removed all the data, remove the element‘s cache
  232. name = "";
  233. for ( name in jQuery.cache[ id ] )
  234. break;
  235. if ( !name )
  236. jQuery.removeData( elem );
  237. }
  238. // Otherwise, we want to remove all of the element‘s data
  239. } else {
  240. // Clean up the element expando
  241. try {
  242. //IE不能直接用delete去移除,要用removeAttribute
  243. delete elem[ expando ];
  244. } catch(e){
  245. // IE has trouble directly removing the expando
  246. // but it‘s ok with using removeAttribute
  247. if ( elem.removeAttribute )
  248. elem.removeAttribute( expando );
  249. }
  250. // Completely remove the data cache
  251. //用缓存体中把其索引值也移掉
  252. delete jQuery.cache[ id ];
  253. }
  254. },
  255. //缓存元素的类组数属性
  256. //可读写
  257. queue: function( elem, type, data ) {
  258. if ( elem ){
  259. type = (type || "fx") + "queue";
  260. var q = jQuery.data( elem, type );
  261. if ( !q || jQuery.isArray(data) )
  262. //q是数组
  263. q = jQuery.data( elem, type, jQuery.makeArray(data) );
  264. else if( data )
  265. q.push( data );
  266. }
  267. return q;
  268. },
  269. //对元素的类数组缓存进行dequeue(也就是shift)
  270. dequeue: function( elem, type ){
  271. var queue = jQuery.queue( elem, type ),
  272. fn = queue.shift();
  273. if( !type || type === "fx" )
  274. fn = queue[0];
  275. if( fn !== undefined )
  276. fn.call(elem);
  277. }
  278. });
  279. //让jQuery对象也能获得这种缓存能力
  280. //都是用上面静态方法实现,最终的缓存体还是jQuery.cache
  281. jQuery.fn.extend({
  282. data: function( key, value ){
  283. var parts = key.split(".");
  284. parts[1] = parts[1] ? "." + parts[1] : "";
  285. if ( value === undefined ) {
  286. var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
  287. if ( data === undefined && this.length )
  288. data = jQuery.data( this[0], key );
  289. return data === undefined && parts[1] ?
  290. this.data( parts[0] ) :
  291. data;
  292. } else
  293. return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){
  294. jQuery.data( this, key, value );
  295. });
  296. },
  297. removeData: function( key ){
  298. return this.each(function(){
  299. jQuery.removeData( this, key );
  300. });
  301. },
  302. queue: function(type, data){
  303. if ( typeof type !== "string" ) {
  304. data = type;
  305. type = "fx";
  306. }
  307. if ( data === undefined )
  308. return jQuery.queue( this[0], type );
  309. return this.each(function(){
  310. var queue = jQuery.queue( this, type, data );
  311. if( type == "fx" && queue.length == 1 )
  312. queue[0].call(this);
  313. });
  314. },
  315. dequeue: function(type){
  316. return this.each(function(){
  317. jQuery.dequeue( this, type );
  318. });
  319. }
  320. });
  321. <p>今天我开始攻略jQuery的心脏,css选择器。不过Sizzle是如此复杂的东西,我发现不能跟着John Resig的思路一行行读下去,因此下面的代码和jQuery的次序是不一样的。</p>
  322. <p>jQuery 的代码是包含在一个巨大的闭包中,Sizzle又在它里面开辟另一个闭包。它是完全独立于jQuery,jQuery通过find方法来调用 Sizzle。一开始是这几个变量,尤其是那个正则,用于分解我们传入的字符串</p>
  323. <pre class="brush:javascript;gutter:false;toolbar:false">
  324. var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|[‘"][^‘"]*[‘"]|[^[\]‘"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,
  325. done = 0,
  326. toString = Object.prototype.toString;
  327. </pre>
  328. <p>然后我们看其表达式,用于深加工与过滤以及简单的查找:</p>
  329. <pre class="brush:javascript;gutter:false;toolbar:false">
  330. //@author  司徒正美|なさみ|cheng http://www.cnblogs.com/rubylouvre/ All rights reserved
  331. var Expr = Sizzle.selectors = {
  332. order: [ "ID", "NAME", "TAG" ],
  333. match: {
  334. ID: /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
  335. CLASS: /\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
  336. NAME: /\[name=[‘"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)[‘"]*\]/,
  337. ATTR: /\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*([‘"]*)(.*?)\3|)\s*\]/,
  338. TAG: /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,
  339. CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
  340. POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
  341. PSEUDO: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\(([‘"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
  342. },
  343. attrMap: {//一些属性不能直接其HTML名字去取,需要用其在javascript的属性名
  344. "class": "className",
  345. "for": "htmlFor"
  346. },
  347. attrHandle: {
  348. href: function(elem){
  349. return elem.getAttribute("href");
  350. }
  351. },
  352. relative: {
  353. //相邻选择符
  354. "+": function(checkSet, part, isXML){
  355. var isPartStr = typeof part === "string",
  356. isTag = isPartStr && !/\W/.test(part),
  357. isPartStrNotTag = isPartStr && !isTag;
  358. if ( isTag && !isXML ) {
  359. part = part.toUpperCase();
  360. }
  361. for ( var i = 0, l = checkSet.length, elem; i &lt; l; i++ ) {
  362. if ( (elem = checkSet[i]) ) {
  363. while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
  364. checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
  365. elem || false :
  366. elem === part;
  367. }
  368. }
  369. if ( isPartStrNotTag ) {
  370. Sizzle.filter( part, checkSet, true );
  371. }
  372. },
  373. //亲子选择符
  374. "&gt;": function(checkSet, part, isXML){
  375. var isPartStr = typeof part === "string";
  376. if ( isPartStr && !/\W/.test(part) ) {
  377. part = isXML ? part : part.toUpperCase();
  378. for ( var i = 0, l = checkSet.length; i &lt; l; i++ ) {
  379. var elem = checkSet[i];
  380. if ( elem ) {
  381. var parent = elem.parentNode;
  382. checkSet[i] = parent.nodeName === part ? parent : false;
  383. }
  384. }
  385. } else {
  386. for ( var i = 0, l = checkSet.length; i &lt; l; i++ ) {
  387. var elem = checkSet[i];
  388. if ( elem ) {
  389. checkSet[i] = isPartStr ?
  390. elem.parentNode :
  391. elem.parentNode === part;
  392. }
  393. }
  394. if ( isPartStr ) {
  395. Sizzle.filter( part, checkSet, true );
  396. }
  397. }
  398. },
  399. //后代选择符
  400. "": function(checkSet, part, isXML){
  401. var doneName = done++, checkFn = dirCheck;
  402. if ( !part.match(/\W/) ) {
  403. var nodeCheck = part = isXML ? part : part.toUpperCase();
  404. checkFn = dirNodeCheck;
  405. }
  406. checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
  407. },
  408. //兄长选择符
  409. "~": function(checkSet, part, isXML){
  410. var doneName = done++, checkFn = dirCheck;
  411. if ( typeof part === "string" && !part.match(/\W/) ) {
  412. var nodeCheck = part = isXML ? part : part.toUpperCase();
  413. checkFn = dirNodeCheck;
  414. }
  415. checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
  416. }
  417. },
  418. find: {
  419. ID: function(match, context, isXML){
  420. if ( typeof context.getElementById !== "undefined" && !isXML ) {
  421. var m = context.getElementById(match[1]);
  422. return m ? [m] : [];//就算只有一个也放进数组
  423. }
  424. },
  425. NAME: function(match, context, isXML){
  426. if ( typeof context.getElementsByName !== "undefined" ) {
  427. var ret = [], results = context.getElementsByName(match[1]);
  428. for ( var i = 0, l = results.length; i &lt; l; i++ ) {
  429. if ( results[i].getAttribute("name") === match[1] ) {
  430. ret.push( results[i] );
  431. }
  432. }
  433. return ret.length === 0 ? null : ret;
  434. }
  435. },
  436. TAG: function(match, context){
  437. return context.getElementsByTagName(match[1]);
  438. }
  439. },
  440. preFilter: {//这里,如果符合的话都返回字符串
  441. CLASS: function(match, curLoop, inplace, result, not, isXML){
  442. match = " " + match[1].replace(/\\/g, "") + " ";
  443. if ( isXML ) {
  444. return match;
  445. }
  446. for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
  447. if ( elem ) {
  448. //相当于hasClassName
  449. if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) &gt;= 0) ) {
  450. if ( !inplace )
  451. result.push( elem );
  452. } else if ( inplace ) {
  453. curLoop[i] = false;
  454. }
  455. }
  456. }
  457. return false;
  458. },
  459. ID: function(match){
  460. return match[1].replace(/\\/g, "");
  461. },
  462. TAG: function(match, curLoop){
  463. for ( var i = 0; curLoop[i] === false; i++ ){}
  464. return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
  465. },
  466. CHILD: function(match){
  467. //把nth(****)里面的表达式都弄成an+b的样子
  468. if ( match[1] == "nth" ) {
  469. // parse equations like ‘even‘, ‘odd‘, ‘5‘, ‘2n‘, ‘3n+2‘, ‘4n-1‘, ‘-n+6‘
  470. var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
  471. match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
  472. !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
  473. // calculate the numbers (first)n+(last) including if they are negative
  474. match[2] = (test[1] + (test[2] || 1)) - 0;
  475. match[3] = test[3] - 0;
  476. }
  477. // TODO: Move to normal caching system
  478. match[0] = done++;
  479. return match;
  480. },
  481. ATTR: function(match, curLoop, inplace, result, not, isXML){
  482. var name = match[1].replace(/\\/g, "");
  483. if ( !isXML && Expr.attrMap[name] ) {
  484. match[1] = Expr.attrMap[name];
  485. }
  486. if ( match[2] === "~=" ) {
  487. match[4] = " " + match[4] + " ";
  488. }
  489. return match;
  490. },
  491. PSEUDO: function(match, curLoop, inplace, result, not){
  492. if ( match[1] === "not" ) {
  493. // If we‘re dealing with a complex expression, or a simple one
  494. if ( match[3].match(chunker).length &gt; 1 || /^\w/.test(match[3]) ) {
  495. match[3] = Sizzle(match[3], null, null, curLoop);
  496. } else {
  497. var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
  498. if ( !inplace ) {
  499. result.push.apply( result, ret );
  500. }
  501. return false;
  502. }
  503. } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
  504. return true;
  505. }
  506. return match;
  507. },
  508. POS: function(match){
  509. match.unshift( true );
  510. return match;
  511. }
  512. },
  513. filters: {//都是返回布尔值
  514. enabled: function(elem){
  515. //不能为隐藏域
  516. return elem.disabled === false && elem.type !== "hidden";
  517. },
  518. disabled: function(elem){
  519. return elem.disabled === true;
  520. },
  521. checked: function(elem){
  522. return elem.checked === true;
  523. },
  524. selected: function(elem){
  525. // Accessing this property makes selected-by-default
  526. // options in Safari work properly
  527. elem.parentNode.selectedIndex;
  528. return elem.selected === true;
  529. },
  530. parent: function(elem){
  531. //是否是父节点(是,肯定有第一个子节点)
  532. return !!elem.firstChild;
  533. },
  534. empty: function(elem){
  535. //是否为空,一点节点也没有
  536. return !elem.firstChild;
  537. },
  538. has: function(elem, i, match){
  539. return !!Sizzle( match[3], elem ).length;
  540. },
  541. header: function(elem){
  542. //是否是h1,h2,h3,h4,h5,h6
  543. return /h\d/i.test( elem.nodeName );
  544. },
  545. text: function(elem){
  546. //文本域,下面几个相仿,基本上可以归类于属性选择器
  547. return "text" === elem.type;
  548. },
  549. radio: function(elem){
  550. return "radio" === elem.type;
  551. },
  552. checkbox: function(elem){
  553. return "checkbox" === elem.type;
  554. },
  555. file: function(elem){
  556. return "file" === elem.type;
  557. },
  558. password: function(elem){
  559. return "password" === elem.type;
  560. },
  561. submit: function(elem){
  562. return "submit" === elem.type;
  563. },
  564. image: function(elem){
  565. return "image" === elem.type;
  566. },
  567. reset: function(elem){
  568. return "reset" === elem.type;
  569. },
  570. button: function(elem){
  571. return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
  572. },
  573. input: function(elem){
  574. return /input|select|textarea|button/i.test(elem.nodeName);
  575. }
  576. },
  577. setFilters: {//子元素过滤器
  578. first: function(elem, i){
  579. return i === 0;
  580. },
  581. last: function(elem, i, match, array){
  582. return i === array.length - 1;
  583. },
  584. even: function(elem, i){
  585. return i % 2 === 0;
  586. },
  587. odd: function(elem, i){
  588. return i % 2 === 1;
  589. },
  590. lt: function(elem, i, match){
  591. return i &lt; match[3] - 0;
  592. },
  593. gt: function(elem, i, match){
  594. return i &gt; match[3] - 0;
  595. },
  596. nth: function(elem, i, match){
  597. return match[3] - 0 == i;
  598. },
  599. eq: function(elem, i, match){
  600. return match[3] - 0 == i;
  601. }
  602. },
  603. filter: {
  604. PSEUDO: function(elem, match, i, array){
  605. var name = match[1], filter = Expr.filters[ name ];
  606. if ( filter ) {
  607. return filter( elem, i, match, array );
  608. } else if ( name === "contains" ) {
  609. return (elem.textContent || elem.innerText || "").indexOf(match[3]) &gt;= 0;
  610. } else if ( name === "not" ) {
  611. var not = match[3];
  612. for ( var i = 0, l = not.length; i &lt; l; i++ ) {
  613. if ( not[i] === elem ) {
  614. return false;
  615. }
  616. }
  617. return true;
  618. }
  619. },
  620. CHILD: function(elem, match){
  621. var type = match[1], node = elem;
  622. switch (type) {
  623. case ‘only‘:
  624. case ‘first‘:
  625. while (node = node.previousSibling)  {
  626. if ( node.nodeType === 1 ) return false;
  627. }
  628. if ( type == ‘first‘) return true;
  629. node = elem;
  630. case ‘last‘:
  631. while (node = node.nextSibling)  {
  632. if ( node.nodeType === 1 ) return false;
  633. }
  634. return true;
  635. case ‘nth‘:
  636. var first = match[2], last = match[3];
  637. if ( first == 1 && last == 0 ) {
  638. return true;
  639. }
  640. var doneName = match[0],
  641. parent = elem.parentNode;
  642. if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
  643. var count = 0;
  644. for ( node = parent.firstChild; node; node = node.nextSibling ) {
  645. if ( node.nodeType === 1 ) {
  646. node.nodeIndex = ++count;//添加一个私有属性
  647. }
  648. }
  649. parent.sizcache = doneName;
  650. }
  651. var diff = elem.nodeIndex - last;
  652. if ( first == 0 ) {
  653. return diff == 0;//判断是否为第一个子元素
  654. } else {
  655. return ( diff % first == 0 && diff / first &gt;= 0 );
  656. }
  657. }
  658. },
  659. ID: function(elem, match){
  660. return elem.nodeType === 1 && elem.getAttribute("id") === match;
  661. },
  662. TAG: function(elem, match){
  663. return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
  664. },
  665. CLASS: function(elem, match){
  666. return (" " + (elem.className || elem.getAttribute("class")) + " ")
  667. .indexOf( match ) &gt; -1;
  668. },
  669. ATTR: function(elem, match){
  670. var name = match[1],
  671. result = Expr.attrHandle[ name ] ?
  672. Expr.attrHandle[ name ]( elem ) :
  673. elem[ name ] != null ?
  674. elem[ name ] :
  675. elem.getAttribute( name ),
  676. value = result + "",
  677. type = match[2],
  678. check = match[4];
  679. return result == null ?
  680. type === "!=" :
  681. type === "=" ?
  682. value === check :
  683. type === "*=" ?
  684. value.indexOf(check) &gt;= 0 :
  685. type === "~=" ?
  686. (" " + value + " ").indexOf(check) &gt;= 0 :
  687. !check ?
  688. value && result !== false :
  689. type === "!=" ?
  690. value != check :
  691. type === "^=" ?
  692. value.indexOf(check) === 0 :
  693. type === "$=" ?
  694. value.substr(value.length - check.length) === check :
  695. type === "|=" ?
  696. value === check || value.substr(0, check.length + 1) === check + "-" :
  697. false;
  698. },
  699. POS: function(elem, match, i, array){
  700. var name = match[2], filter = Expr.setFilters[ name ];
  701. if ( filter ) {
  702. return filter( elem, i, match, array );
  703. }
  704. }
  705. }
  706. };
  707. var origPOS = Expr.match.POS;
  708. </pre>
  709. <div><img src="http://images.cnblogs.com/cnblogs_com/rubylouvre/205314/o_Sizzle.gif"></div>
  710. <p> 但上图没有完全显现Sizzle复杂的工作机制,它是从左到右工作,加工了一个字符串,查找,然后过滤非元素节点,再跟据其属性或内容或在父元素的顺序过 滤,然后到下一个字符串,这时搜索起点就是上次的结果数组的元素节点,想象一下草根的样子吧。在许多情况下,选择器都是靠工作 的,element.getElementsByTagName(*),获得其一元素的所有子孙,因此Expr中的过滤器特别多。为了过快查找速度,如有 些浏览器已经实现了getElementsByClassName,jQuery也设法把它们利用起来。</p>
  711. <pre class="brush:javascript;gutter:false;toolbar:false">
  712. for ( var type in Expr.match ) {
  713. //重写Expr.match中的正则,利用负向零宽断言让其更加严谨
  714. Expr.match[ type ] = RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
  715. }
  716. </pre>
  717. <p>接着下来我们还是未到时候看上面的主程序,继续看它的辅助方法。</p>
  718. <pre class="brush:javascript;gutter:false;toolbar:false">
  719. //把NodeList HTMLCollection转换成纯数组,如果有第二参数(上次查找的结果),则把它们加入到结果集中
  720. var makeArray = function(array, results) {
  721. array = Array.prototype.slice.call( array );
  722. if ( results ) {
  723. results.push.apply( results, array );
  724. return results;
  725. }
  726. return array;
  727. };
  728. try {
  729. //基本上是用于测试IE的,IE的NodeList HTMLCollection不支持用数组的slice转换为数组
  730. Array.prototype.slice.call( document.documentElement.childNodes );
  731. //这时就要重载makeArray,一个个元素搬入一个空数组中了
  732. } catch(e){
  733. makeArray = function(array, results) {
  734. var ret = results || [];
  735. if ( toString.call(array) === "[object Array]" ) {
  736. Array.prototype.push.apply( ret, array );
  737. } else {
  738. if ( typeof array.length === "number" ) {
  739. for ( var i = 0, l = array.length; i &lt; l; i++ ) {
  740. ret.push( array[i] );
  741. }
  742. } else {
  743. for ( var i = 0; array[i]; i++ ) {
  744. ret.push( array[i] );
  745. }
  746. }
  747. }
  748. return ret;
  749. };
  750. }
  751. <p>在Sizzle中有许多有用的辅助方法,我们继续一个个看。其中涉及许多BUG的修正以及一些很少见的API。</p>
  752. <pre class="brush:javascript;gutter:false;toolbar:false">
  753. //@author  司徒正美|なさみ|cheng http://www.cnblogs.com/rubylouvre/ All rights reserved
  754. var sortOrder;//比较两个元素在页面上的顺序,返回正数,0,负数
  755. //如果支持compareDocumentPosition方法,新锐的标准浏览器都支持
  756. //我在《javascript contains方法》一文中有详细介绍
  757. //http://www.cnblogs.com/rubylouvre/archive/2009/10/14/1583523.html
  758. if ( document.documentElement.compareDocumentPosition ) {
  759. sortOrder = function( a, b ) {
  760. //节点a 在节点b 之前,
  761. var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
  762. if ( ret === 0 ) {
  763. hasDuplicate = true;
  764. }
  765. return ret;
  766. };
  767. //用于IE
  768. //sourceIndex是指元素在NodeList中的位置
  769. } else if ( "sourceIndex" in document.documentElement ) {
  770. sortOrder = function( a, b ) {
  771. var ret = a.sourceIndex - b.sourceIndex;
  772. if ( ret === 0 ) {
  773. hasDuplicate = true;
  774. }
  775. return ret;
  776. };
  777. //用于旧式的标准游览器
  778. } else if ( document.createRange ) {
  779. sortOrder = function( a, b ) {
  780. var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
  781. aRange.selectNode(a);
  782. aRange.collapse(true);
  783. bRange.selectNode(b);
  784. bRange.collapse(true);
  785. //比较两个selection的位置
  786. //https://developer.mozilla.org/en/DOM/range.compareBoundaryPoints
  787. var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
  788. if ( ret === 0 ) {
  789. hasDuplicate = true;
  790. }
  791. return ret;
  792. };
  793. }
  794. </pre>
  795. <p>比较元素位置在IE还可以用uniqueNumber,都是自上至下分配数字。</p>
  796. <p>下面对getElementById,getElementsByTagName,getElementsByClassName, querySelectorAll 进行调整。</p>
  797. <pre class="brush:javascript;gutter:false;toolbar:false">
  798. //在getElementById(XXX)在IE中有bug,它会找第一个属性name或id等于XXX的元素,
  799. //尤其是在表单元素中,它们通常都带有name属性
  800. (function(){
  801. // We‘re going to inject a fake input element with a specified name
  802. var form = document.createElement("form"),
  803. id = "script" + (new Date).getTime();
  804. form.innerHTML = "&lt;input name=‘" + id + "‘/&gt;";
  805. // Inject it into the root element, check its status, and remove it quickly
  806. var root = document.documentElement;
  807. root.insertBefore( form, root.firstChild );
  808. // The workaround has to do additional checks after a getElementById
  809. // Which slows things down for other browsers (hence the branching)
  810. if ( !!document.getElementById( id ) ) {
  811. //重载一下Expr.find.ID
  812. Expr.find.ID = function(match, context, isXML){
  813. if ( typeof context.getElementById !== "undefined" && !isXML ) {
  814. var m = context.getElementById(match[1]);
  815. //确定此元素是否显式为id赋值
  816. return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" &&
  817. m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
  818. }
  819. };
  820. Expr.filter.ID = function(elem, match){
  821. //确定此元素是否显式为id赋值
  822. var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
  823. return elem.nodeType === 1 && node && node.nodeValue === match;
  824. };
  825. }
  826. root.removeChild( form );
  827. })();
  828. (function(){
  829. // Check to see if the browser returns only elements
  830. // when doing getElementsByTagName("*")
  831. // Create a fake element
  832. var div = document.createElement("div");
  833. div.appendChild( document.createComment("") );
  834. // Make sure no comments are found
  835. if ( div.getElementsByTagName("*").length &gt; 0 ) {
  836. //重载Expr.find.TAG
  837. Expr.find.TAG = function(match, context){
  838. var results = context.getElementsByTagName(match[1]);
  839. // Filter out possible comments
  840. //返回其所有元素节点后代,组成纯数组
  841. if ( match[1] === "*" ) {
  842. var tmp = [];
  843. for ( var i = 0; results[i]; i++ ) {
  844. if ( results[i].nodeType === 1 ) {
  845. tmp.push( results[i] );
  846. }
  847. }
  848. results = tmp;
  849. }
  850. return results;
  851. };
  852. }
  853. // Check to see if an attribute returns normalized href attributes
  854. //处理href属性,如果第二个参数,IE返回的是绝对路径
  855. div.innerHTML = "&lt;a href=‘#‘&gt;&lt;/a&gt;";
  856. if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
  857. div.firstChild.getAttribute("href") !== "#" ) {
  858. Expr.attrHandle.href = function(elem){
  859. return elem.getAttribute("href", 2);
  860. };
  861. }
  862. })();
  863. if ( document.querySelectorAll ) (function(){
  864. //创建一个元素片段&lt;div&gt;&lt;p class=‘TEST‘&gt;&lt;/p&gt;&lt;/div&gt;
  865. //用querySelectorAll看看能否正确找到这个p元素
  866. var oldSizzle = Sizzle, div = document.createElement("div");
  867. div.innerHTML = "&lt;p class=‘TEST‘&gt;&lt;/p&gt;";
  868. // Safari can‘t handle uppercase or unicode characters when
  869. // in quirks mode.
  870. if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
  871. return;
  872. }
  873. //如果能,就用querySelectorAll重载整个Sizzle引擎,效率最高!!!
  874. Sizzle = function(query, context, extra, seed){
  875. context = context || document;
  876. // Only use querySelectorAll on non-XML documents
  877. // (ID selectors don‘t work in non-HTML documents)
  878. if ( !seed && context.nodeType === 9 && !isXML(context) ) {
  879. try {
  880. return makeArray( context.querySelectorAll(query), extra );
  881. } catch(e){}
  882. }
  883. return oldSizzle(query, context, extra, seed);
  884. };
  885. Sizzle.find = oldSizzle.find;
  886. Sizzle.filter = oldSizzle.filter;
  887. Sizzle.selectors = oldSizzle.selectors;
  888. Sizzle.matches = oldSizzle.matches;
  889. })();
  890. if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
  891. // 创建一个元素片段&lt;div&gt;&lt;div class=‘test e‘&gt;&lt; /div&gt;&lt;div class=‘test‘&gt;&lt;/div&gt;&lt;/div&gt;
  892. //用getElementsByClassName看看能否正确找到这两个div元素
  893. var div = document.createElement("div");
  894. div.innerHTML = "&lt;div class=‘test e‘&gt;&lt;/div&gt;&lt;div class=‘test‘&gt;&lt;/div&gt;";
  895. // Opera can‘t find a second classname (in 9.6)
  896. if ( div.getElementsByClassName("e").length === 0 )
  897. return;
  898. // Safari caches class attributes, doesn‘t catch changes (in 3.2)
  899. div.lastChild.className = "e";
  900. if ( div.getElementsByClassName("e").length === 1 )
  901. return;
  902. //重新调整与CLASS有关的逻辑
  903. Expr.order.splice(1, 0, "CLASS");
  904. Expr.find.CLASS = function(match, context, isXML) {
  905. if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
  906. return context.getElementsByClassName(match[1]);
  907. }
  908. };
  909. })();
  910. </pre>
  911. <pre class="brush:javascript;gutter:false;toolbar:false">
  912. //这东西用于后代选择器与兄长选择器,取得某范围中所有元素,并且防止重复取得
  913. function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
  914. var sibDir = dir == "previousSibling" && !isXML;
  915. //checkSet为元素集合,doneName为数字
  916. for ( var i = 0, l = checkSet.length; i &lt; l; i++ ) {
  917. var elem = checkSet[i];
  918. if ( elem ) {
  919. if ( sibDir && elem.nodeType === 1 ){
  920. elem.sizcache = doneName;//设置一标记,以后有与它值相等的不重复取
  921. elem.sizset = i;
  922. }
  923. elem = elem[dir];
  924. var match = false;
  925. while ( elem ) {
  926. if ( elem.sizcache === doneName ) {//比较是否相等
  927. match = checkSet[elem.sizset];
  928. break;
  929. }
  930. if ( elem.nodeType === 1 && !isXML ){
  931. elem.sizcache = doneName;
  932. elem.sizset = i;
  933. }
  934. if ( elem.nodeName === cur ) {
  935. match = elem;
  936. break;
  937. }
  938. elem = elem[dir];
  939. }
  940. checkSet[i] = match;
  941. }
  942. }
  943. }
  944. //和上面功能差不多,不知是否出于兼容以前版本的需要……
  945. function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
  946. var sibDir = dir == "previousSibling" && !isXML;
  947. for ( var i = 0, l = checkSet.length; i &lt; l; i++ ) {
  948. var elem = checkSet[i];
  949. if ( elem ) {
  950. if ( sibDir && elem.nodeType === 1 ) {
  951. elem.sizcache = doneName;
  952. elem.sizset = i;
  953. }
  954. elem = elem[dir];
  955. var match = false;
  956. while ( elem ) {
  957. if ( elem.sizcache === doneName ) {
  958. match = checkSet[elem.sizset];
  959. break;
  960. }
  961. if ( elem.nodeType === 1 ) {
  962. if ( !isXML ) {
  963. elem.sizcache = doneName;
  964. elem.sizset = i;
  965. }
  966. if ( typeof cur !== "string" ) {
  967. if ( elem === cur ) {
  968. match = true;
  969. break;
  970. }
  971. } else if ( Sizzle.filter( cur, [elem] ).length &gt; 0 ) {
  972. match = elem;
  973. break;
  974. }
  975. }
  976. elem = elem[dir];
  977. }
  978. checkSet[i] = match;
  979. }
  980. }
  981. }
  982. </pre>
  983. <pre class="brush:javascript;gutter:false;toolbar:false">
  984. //判断一个元素是否包含另一个元素
  985. //http://www.cnblogs.com/rubylouvre/archive/2009/10/14/1583523.html
  986. var contains = document.compareDocumentPosition ?  function(a, b){
  987. return a.compareDocumentPosition(b) & 16;
  988. } : function(a, b){
  989. return a !== b && (a.contains ? a.contains(b) : true);
  990. };
  991. //判断是否为XML
  992. var isXML = function(elem){
  993. return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
  994. !!elem.ownerDocument && isXML( elem.ownerDocument );
  995. };
  996. //主要是处理结构伪类中的子元素过滤器
  997. var posProcess = function(selector, context){
  998. var tmpSet = [], later = "", match,
  999. root = context.nodeType ? [context] : context;
  1000. // Position selectors must be done after the filter
  1001. // And so must :not(positional) so we move all PSEUDOs to the end
  1002. while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
  1003. later += match[0];
  1004. selector = selector.replace( Expr.match.PSEUDO, "" );
  1005. }
  1006. //如果不是在亲子中选择,就是在它的所有后代中选择“*”
  1007. selector = Expr.relative[selector] ? selector + "*" : selector;
  1008. //回调Sizzle
  1009. for ( var i = 0, l = root.length; i < l; i++ ) {
  1010. Sizzle( selector, root[i], tmpSet );
  1011. }
  1012. return Sizzle.filter( later, tmpSet );
  1013. };
  1014. <p>今天把jQuery的Sizzle选择器引擎讲完。最后给出其大体的工作流程。这东西非常复杂,不要妄图看一遍就明白了。无论看懂与否,多看点源码,还是有裨益的。至少在处理循环结构上有收获吧。</p>
  1015. <pre class="brush:javascript;gutter:false;toolbar:false">
  1016. //@author  司徒正美|なさみ|cheng http://www.cnblogs.com/rubylouvre/ All rights reserved
  1017. // EXPOSE
  1018. jQuery.find = Sizzle;
  1019. jQuery.filter = Sizzle.filter;
  1020. jQuery.expr = Sizzle.selectors;
  1021. //以:开头许多都是自定义伪类
  1022. jQuery.expr[":"] = jQuery.expr.filters;
  1023. //css属性display引起的元素不可见
  1024. Sizzle.selectors.filters.hidden = function(elem){
  1025. return elem.offsetWidth === 0 || elem.offsetHeight === 0;
  1026. };
  1027. //css属性display引起的元素不可见
  1028. Sizzle.selectors.filters.visible = function(elem){
  1029. return elem.offsetWidth &gt; 0 || elem.offsetHeight &gt; 0;
  1030. };
  1031. //是否在运动中
  1032. Sizzle.selectors.filters.animated = function(elem){
  1033. return jQuery.grep(jQuery.timers, function(fn){
  1034. return elem === fn.elem;
  1035. }).length;
  1036. };
  1037. //重载jQuery.multiFilter
  1038. jQuery.multiFilter = function( expr, elems, not ) {
  1039. if ( not ) {
  1040. expr = ":not(" + expr + ")";
  1041. }
  1042. return Sizzle.matches(expr, elems);
  1043. };
  1044. //把路径上的元素放到结果上,dir为parentNode,previousSibling,nextSilbing
  1045. jQuery.dir = function( elem, dir ){
  1046. var matched = [], cur = elem[dir];
  1047. while ( cur && cur != document ) {
  1048. if ( cur.nodeType == 1 )
  1049. matched.push( cur );
  1050. cur = cur[dir];
  1051. }
  1052. return matched;
  1053. };
  1054. //在内部调用result好像都为2,dir为previousSibling,nextSilbing
  1055. //用于子元素过滤
  1056. jQuery.nth = function(cur, result, dir, elem){
  1057. result = result || 1;
  1058. var num = 0;
  1059. //如果cur为undefined中止循环
  1060. for ( ; cur; cur = cur[dir] )
  1061. if ( cur.nodeType == 1 && ++num == result )
  1062. break;
  1063. return cur;
  1064. };
  1065. //查找不等于elem的兄弟元素节点
  1066. jQuery.sibling = function(n, elem){
  1067. var r = [];
  1068. for ( ; n; n = n.nextSibling ) {
  1069. if ( n.nodeType == 1 && n != elem )
  1070. r.push( n );
  1071. }
  1072. return r;
  1073. };
  1074. return;
  1075. window.Sizzle = Sizzle;
  1076. </pre>
  1077. <p>好了,回头看Sizzle的主程序部分:</p>
  1078. <pre class="brush:javascript;gutter:false;toolbar:false">
  1079. Sizzle.find = function(expr, context, isXML){
  1080. var set, match;
  1081. if ( !expr ) {//如果不是字符串表达式则返回空数组
  1082. return [];
  1083. }
  1084. for ( var i = 0, l = Expr.order.length; i &lt; l; i++ ) {
  1085. var type = Expr.order[i], match;//按照ID NAME TAG的优先级顺序执行
  1086. //这里可以想象一下
  1087. //match = "#aaa".exec( /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/)
  1088. //然后检测match是否为空数组,空数组相当于false
  1089. if ( (match = Expr.match[ type ].exec( expr )) ) {
  1090. //ID的正则 /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/
  1091. var left = RegExp.leftContext
  1092. //如果不是一步到位,是复杂的表达式,需要多次查找与筛选
  1093. if ( left.substr( left.length - 1 ) !== "\\" ) {
  1094. //把换行符去掉,得到正常的字段
  1095. //如"#id12\
  1096. //34"
  1097. //去掉后,就得到"#id1234"
  1098. match[1] = (match[1] || "").replace(/\\/g, "");
  1099. set = Expr.find[ type ]( match, context, isXML );
  1100. if ( set != null ) {
  1101. //移除相应部分的表达,
  1102. // 如#aaa ee,得到ID对应的元素后,把#aaa去掉,
  1103. //然后用Expr的表达式来匹配剩下的部分
  1104. expr = expr.replace( Expr.match[ type ], "" );
  1105. break;
  1106. }
  1107. }
  1108. }
  1109. }
  1110. if ( !set ) {
  1111. //返回所有后代
  1112. set = context.getElementsByTagName("*");
  1113. }
  1114. return {//返回一个对象
  1115. set: set,
  1116. expr: expr
  1117. };
  1118. };
  1119. </pre>
  1120. <pre class="brush:javascript;gutter:false;toolbar:false">
  1121. Sizzle.filter = function(expr, set, inplace, not){
  1122. var old = expr, result = [], curLoop = set, match, anyFound,
  1123. isXMLFilter = set && set[0] && isXML(set[0]);
  1124. while ( expr && set.length ) {
  1125. for ( var type in Expr.filter ) {
  1126. //这是Expr.filter中的键值对
  1127. //PSEUDO: function(elem, match, i, array){},
  1128. //CHILD: function(elem, match){},
  1129. //ID: function(elem, match){},
  1130. //TAG: function(elem, match){},
  1131. //CLASS: function(elem, match){},
  1132. //ATTR: function(elem, match){},
  1133. //POS: function(elem, match, i, array){}
  1134. if ( (match = Expr.match[ type ].exec( expr )) != null ) {//match为数组
  1135. var filter = Expr.filter[ type ], found, item;//filter这函数
  1136. anyFound = false;
  1137. if ( curLoop == result ) {//如果结果集为空数组,就让result = [];
  1138. result = [];
  1139. }
  1140. if ( Expr.preFilter[ type ] ) {
  1141. //这是Expr.preFilter中的键值对
  1142. //CLASS: function(match, curLoop, inplace, result, not, isXML){},
  1143. //ID: function(match){},
  1144. //TAG: function(match, curLoop){},
  1145. //CHILD: function(match){ },
  1146. //ATTR: function(match, curLoop, inplace, result, not, isXML){},
  1147. //PSEUDO: function(match, curLoop, inplace, result, not){ },
  1148. //POS: function(match){}
  1149. //preFilter与filter的功能不同,preFilter对字符串进行调整,好让选择器能找到元素
  1150. //filter对查找到的元素或元素数组进行筛选
  1151. match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
  1152. if ( !match ) {//如果返回的是false
  1153. anyFound = found = true;//就把anyFound与found标记为true
  1154. } else if ( match === true ) {
  1155. continue;
  1156. }
  1157. }
  1158. if ( match ) {
  1159. for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
  1160. if ( item ) {
  1161. //检测元素是否符合要求
  1162. found = filter( item, match, i, curLoop );
  1163. var pass = not ^ !!found;
  1164. if ( inplace && found != null ) {
  1165. if ( pass ) {
  1166. anyFound = true;
  1167. } else {
  1168. curLoop[i] = false;
  1169. }
  1170. } else if ( pass ) {
  1171. result.push( item );//符合要求就放到结果数组中
  1172. anyFound = true;
  1173. }
  1174. }
  1175. }
  1176. }
  1177. if ( found !== undefined ) {
  1178. if ( !inplace ) {
  1179. curLoop = result;//结果数组将作为一下次要遍历的元素集合返回
  1180. }
  1181. //移除用户输入字符串已查找了的那一部分表达式
  1182. expr = expr.replace( Expr.match[ type ], "" );
  1183. if ( !anyFound ) {
  1184. return [];
  1185. }
  1186. break;
  1187. }
  1188. }
  1189. }
  1190. // Improper expression
  1191. if ( expr == old ) {
  1192. if ( anyFound == null ) {
  1193. throw "Syntax error, unrecognized expression: " + expr;
  1194. } else {
  1195. break;
  1196. }
  1197. }
  1198. old = expr;
  1199. }
  1200. return curLoop;
  1201. };
  1202. </pre>
  1203. <p>主程序:</p>
  1204. <pre class="brush:javascript;gutter:false;toolbar:false">
  1205. var Sizzle = function(selector, context, results, seed) {
  1206. results = results || [];
  1207. context = context || document;
  1208. if ( context.nodeType !== 1 && context.nodeType !== 9 )
  1209. return [];//context必须为DOM元素或document,要不返回空数组
  1210. if ( !selector || typeof selector !== "string" ) {
  1211. return results;//selector必须存在并且为字符串,否则返回上次循环的结果集
  1212. }
  1213. var parts = [], m, set, checkSet, check, mode, extra, prune = true;
  1214. // Reset the position of the chunker regexp (start from head)
  1215. chunker.lastIndex = 0;
  1216. while ( (m = chunker.exec(selector)) !== null ) {
  1217. parts.push( m[1] );
  1218. if ( m[2] ) {
  1219. extra = RegExp.rightContext;//匹配内容的右边归入extra
  1220. break;
  1221. }
  1222. }
  1223. //POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
  1224. if ( parts.length &gt; 1 && origPOS.exec( selector ) ) {
  1225. //处理E F   E &gt; F    E + F   E ~ F
  1226. if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
  1227. //这里的parts[0]肯定不是“”,亦即不会是后代选择器
  1228. set = posProcess( parts[0] + parts[1], context );
  1229. } else {
  1230. set = Expr.relative[ parts[0] ] ?
  1231. [ context ] :
  1232. Sizzle( parts.shift(), context );
  1233. while ( parts.length ) {
  1234. selector = parts.shift()
  1235. if ( Expr.relative[ selector ] )
  1236. selector += parts.shift();
  1237. set = posProcess( selector, set );
  1238. }
  1239. }
  1240. } else {
  1241. var ret = seed ?
  1242. {
  1243. expr: parts.pop(),
  1244. set: makeArray(seed)
  1245. } :
  1246. Sizzle.find( parts.pop(), parts.length === 1 && context.parentNode ? context.parentNode : context, isXML(context) );
  1247. set = Sizzle.filter( ret.expr, ret.set );
  1248. if ( parts.length &gt; 0 ) {
  1249. checkSet = makeArray(set);
  1250. } else {
  1251. prune = false;
  1252. }
  1253. while ( parts.length ) {//倒序的while循环比for循环快
  1254. var cur = parts.pop(), pop = cur;
  1255. if ( !Expr.relative[ cur ] ) {
  1256. cur = "";
  1257. } else {
  1258. pop = parts.pop();
  1259. }
  1260. if ( pop == null ) {
  1261. pop = context;
  1262. }
  1263. Expr.relative[ cur ]( checkSet, pop, isXML(context) );
  1264. }
  1265. }
  1266. if ( !checkSet ) {
  1267. checkSet = set;
  1268. }
  1269. if ( !checkSet ) {
  1270. throw "Syntax error, unrecognized expression: " + (cur || selector);
  1271. }
  1272. //数组化NodeList,并加入结果集中
  1273. if ( toString.call(checkSet) === "[object Array]" ) {
  1274. if ( !prune ) {
  1275. results.push.apply( results, checkSet );
  1276. } else if ( context.nodeType === 1 ) {
  1277. for ( var i = 0; checkSet[i] != null; i++ ) {
  1278. if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
  1279. results.push( set[i] );
  1280. }
  1281. }
  1282. } else {
  1283. for ( var i = 0; checkSet[i] != null; i++ ) {
  1284. if ( checkSet[i] && checkSet[i].nodeType === 1 ) {//确保是元素节点
  1285. results.push( set[i] );
  1286. }
  1287. }
  1288. }
  1289. } else {
  1290. makeArray( checkSet, results );
  1291. }
  1292. if ( extra ) {
  1293. Sizzle( extra, context, results, seed );
  1294. if ( sortOrder ) {
  1295. hasDuplicate = false;
  1296. results.sort(sortOrder);//重排结果集中的DOM元素,按照原来在网页先后顺序排列
  1297. if ( hasDuplicate ) {
  1298. for ( var i = 1; i &lt; results.length; i++ ) {//确保没有重复的DOM元素,方法比较垃圾
  1299. if ( results[i] === results[i-1] ) {
  1300. results.splice(i--, 1);
  1301. }
  1302. }
  1303. }
  1304. }
  1305. }
  1306. return results;
  1307. };
  1308. </pre>
  1309. <p>最后重新说一下其逻辑:</p>
  1310. <ol>
  1311. <li>首先用一个叫chunker的强大正则,把诸如 var str = " #div  ,  h1#id\<br />
  1312. dd.class > span[dd=‘22222 > 3233‘] ul+ li,  .class:contain(\"你的+ 999\"),strong span ";这样的字符串,Sizzle称之为selector的东西,分解成一个数组。<img src="http://images.cnblogs.com/cnblogs_com/rubylouvre/202680/o_selectors.gif" /></li>
  1313. <li> 接着对上下文的内容进行判断,确保其为DOM元素或document,否则返回空数组。然后判断selector是否为字符串,由于Sizzle会不断递 归调用,selector会越来越短的,直到为零。这些越来越短的selector其实也是第一次chunker 分解的结果之一。不过它们都有可能g再 遭分解。每一次循环,这些分解了的字符串都会经过筛选(非空字符),放入parts数组中。</li>
  1314. <li> 这些selector最先会判断一下,是否为亲子兄长相邻后代等关系选择器。由于第一次chunker把大部分空白消灭了,造成了一个不幸的结果,把后代 选择器也消灭了。因此必须补上后代选择器。详见后面posProcess的“selector + "*"”操作。</li>
  1315. <li>在选择器中,也亦即id,tag,name具有查找能力,在标准浏览器中重载了class部分,让getElementsByClassName也能工作。如果querySelectorAll能工作最好不过,整个Sizzle被重载了。总而言之,Sizzle.find所做的工作比较少,它是按[ "ID", "NAME", "TAG" ]的优先级查找元素的。不过在这之前,先要调用Expr.preFilter把连字符"\"造 成的字符串破坏进行修复了。如上面的例子,h1#iddd由于中间的连字符串被切成两个部分,成了数组中的两个元素h1#dd与dd。显然这样查找会找不 到dd这个ID,后面查找所有dd元素也是错误的,因此必须把它们重新整合成一个元素h1#dddd。</li>
  1316. <li> 根据id,name与tag找到这些元素后,下一个循环就是找它们的子元素或后代元素了,所以Sizzle才会急冲冲地修复后代选择器的问题。至于筛 选,Expr有大量的方法来进行。最后是重新排序与去除重复选中的元素,以结果集返回。</li>
  1317. </ol>
时间: 2024-10-25 06:23:32

jQuery源码学习笔记五 六 七 八 转的相关文章

菜鸟的jQuery源码学习笔记(二)

jQuery对象是使用构造函数和原型模式相结合的方式创建的.现在来看看jQuery的原型对象jQuery.prototype: 1 jQuery.fn = jQuery.prototype = { 2 //成员变量和方法 3 } 这里给原型对象起了一个别名叫做jQuery.fn.要注意的是这个jQuery.fn可不是jQuery对象的属性,而是jQuery构造方法本身的属性,它是不会传给它所创建的对象的.如果你在控制台敲$().fn的话输出的结果会是undefined.接下来看看原型对象里面有些

jQuery源码学习笔记:总体架构

1.1.自调用匿名函数: (function( window, undefined ) { // jquery code })(window); 这是一个自调用匿名函数,第一个括号内是一个匿名函数,第二个括号立即执行,传参是window. 1.为什么有自调用匿名函数? 通过定义匿名函数,创建了一个"私有"空间,jQuery必须保证创建的变量不能和导入它的程序发生冲突. 2.为什么传入window? 传入window使得window由全局变量变成局部变量,jQuery访问window时,

jQuery源码学习笔记:构造jQuery对象

3.1源码结构: (function( window, undefined ) { var jQuery = (function() { // 构建jQuery对象 var jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context, rootjQuery ); } // jQuery对象原型 jQuery.fn = jQuery.prototype = { constructor:

jQuery源码学习笔记:扩展工具函数

// 扩展工具函数 jQuery.extend({ // http://www.w3school.com.cn/jquery/core_noconflict.asp // 释放$的 jQuery 控制权 // 许多 JavaScript 库使用 $ 作为函数或变量名,jQuery 也一样. // 在 jQuery 中,$ 仅仅是 jQuery 的别名,因此即使不使用 $ 也能保证所有功能性. // 假如我们需要使用 jQuery 之外的另一 JavaScript 库,我们可以通过调用 $.noC

jQuery源码学习笔记(1)

在慕课网上学习jQuery源码,做一些笔记小研究. 第1章 节点遍历 第2章 文档处理 第3章 元素操作 第4章 样式操作 第5章 事件体系 第6章 数据交互 第7章 动画引擎 首先瞅瞅目录,大概可以了解一下这个是怎么讲的QAQ. 今天学习的是节点遍历. 遍历的对象主要是:1 祖先 2 同胞兄弟 3 后代 4 过滤 我们先看祖先. .parent()方法允许我们能够在DOM树中搜索到这些元素的父级元素,从有序的向上匹配元素,并根据匹配的元素创建一个新的 jQuery 对象. .parents()

jquery源码学习笔记(一)jQuery的无new构建

本人是一名.net程序员..... 你一个.net coder 看什么jQuery 源码啊? 原因吗,很简单.技多不压身吗(麻蛋,前端工作好高...羡慕). 我一直都很喜欢JavaScript,废话不多说了,直接切入正题. 最近看了好几篇jQuery 源码的文章,对于jQuery的无new构建  很是不解. 查了很多资料,总算是搞明白了. jQuery的无new构建 jQuery框架的核心就是从HTML文档中匹配元素并对其执行操作. 回想一下使用 jQuery 的时候,实例化一个 jQuery

2016年11月2日——jQuery源码学习笔记

1.jQuery()函数,即$().有四种不同的调用方式. (1)传递CSS选择器(字符串)给$()方法,返回当前文档中匹配该选择器的元素集.可选第二个参数,一个元素或jQuery对象,定义元素查询的起始点,称为上下文(context),这时返回的是该特定元素或元素集的子元素中匹配选择器的部分. (2)传递一个Element.Document或Window对象给$()方法,$()将它们封装为jQuery对象并返回,这样就可以使用jQuery方法来操作这些元素而不用使用原生DOM方法 (3)传递H

菜鸟的jQuery源码学习笔记(三)

1 each: function(callback, args) { 2 return jQuery.each(this, callback, args); 3 }, each:这个调用了jQuery.each方法,来遍历当前集合.我们先来看看jQuery.each方法: //args是一个数组 each: function(obj, callback, args) { var value, i = 0, length = obj.length, isArray = isArraylike(ob

yii2源码学习笔记(十六)

Module类的最后代码 1 /** 2 * Registers sub-modules in the current module. 3 * 注册子模块到当前模块 4 * Each sub-module should be specified as a name-value pair, where 5 * name refers to the ID of the module and value the module or a configuration 6 * array that can