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

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