zoukankan      html  css  js  c++  java
  • jQuery源码学习笔记五 六 七 八 转

    1. <p>在正式深入jQuery的核心功能选择器之前,还有一些方法,基本都是数组方法,用于遴选更具体的需求,如获得某个元素的所有祖选元素啦,等等。接着是其缓存机制data。</p>  
    2.   
    3. <pre class="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="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="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="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: /#((?:[wu00c0-uFFFF_-]|\.)+)/,  
    392.         CLASS: /.((?:[wu00c0-uFFFF_-]|\.)+)/,  
    393.         NAME: /[name=['"]*((?:[wu00c0-uFFFF_-]|\.)+)['"]*]/,  
    394.         ATTR: /[s*((?:[wu00c0-uFFFF_-]|\.)+)s*(?:(S?=)s*(['"]*)(.*?)3|)s*]/,  
    395.         TAG: /^((?:[wu00c0-uFFFF*_-]|\.)+)/,  
    396.         CHILD: /:(only|nth|last|first)-child(?:((even|odd|[dn+-]*)))?/,  
    397.         POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:((d*)))?(?=[^-]|$)/,  
    398.         PSEUDO: /:((?:[wu00c0-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 /hd/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="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="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="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="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="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="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="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="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( /#((?:[wu00c0-uFFFF_-]|\.)+)/)  
    1236.                //然后检测match是否为空数组,空数组相当于false  
    1237.                 if ( (match = Expr.match[ type ].exec( expr )) ) {  
    1238.                  //ID的正则 /#((?:[wu00c0-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="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="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> 
  • 相关阅读:
    团队冲刺第二天
    电梯演讲的准备——冰淇淋第一个项目NABCD分析
    团队冲刺第四天
    团队冲刺第六天
    团队冲刺第三天
    团队冲刺第一天
    XmlDocument类的WriteContentTo和WriteTo方法
    从一场DOTA对战中发现的哲理,也做为对2012年的展望
    String.Trim()真相大揭秘
    SQL Server 2008数据库维护计划
  • 原文地址:https://www.cnblogs.com/shsgl/p/4397852.html
Copyright © 2011-2022 走看看