jQuery源码学习笔记五 六 七 八 转
Js代码
- <p>在正式深入jQuery的核心功能选择器之前,还有一些方法,基本都是数组方法,用于遴选更具体的需求,如获得某个元素的所有祖选元素啦,等等。接着是其缓存机制data。</p>
- <pre class="brush:javascript;gutter:false;toolbar:false">
- //@author 司徒正美|なさみ|cheng http://www.cnblogs.com/rubylouvre/ All rights reserved
- //去除两边的空白
- trim: function( text ) {
- return (text || "").replace( /^\s+|\s+$/g, "" );
- },
- //转换成数组,很大众的方法
- makeArray: function( array ) {
- var ret = [];
- if( array != null ){
- var i = array.length;
- // The window, strings (and functions) also have ‘length‘
- if( i == null || typeof array === "string" || jQuery.isFunction(array) || array.setInterval )
- ret[0] = array;//就只有一元素
- else
- while( i )//处理数组
- ret[--i] = array[i];
- }
- return ret;
- },
- //判断是否在数组中,类似indexOf
- inArray: function( elem, array ) {
- for ( var i = 0, length = array.length; i < length; i++ )
- // Use === because on IE, window == document
- if ( array[ i ] === elem )
- return i;
- return -1;
- },
- //把新元素或第二个数组加入第一个数组中
- //类似数组的concat
- merge: function( first, second ) {
- // We have to loop this way because IE & Opera overwrite the length
- // expando of getElementsByTagName
- var i = 0, elem, pos = first.length;
- // Also, we need to make sure that the correct elements are being returned
- // (IE returns comment nodes in a ‘*‘ query)
- if ( !jQuery.support.getAll ) {
- while ( (elem = second[ i++ ]) != null )
- if ( elem.nodeType != 8 )
- first[ pos++ ] = elem;
- } else
- while ( (elem = second[ i++ ]) != null )
- first[ pos++ ] = elem;
- return first;
- },
- //过滤重复元素,用done这个普通对象做过滤器(因为键如果同名将被覆盖掉)
- unique: function( array ) {
- var ret = [], done = {};
- try {
- for ( var i = 0, length = array.length; i < length; i++ ) {
- var id = jQuery.data( array[ i ] );
- if ( !done[ id ] ) {
- done[ id ] = true;
- ret.push( array[ i ] );
- }
- }
- } catch( e ) {
- ret = array;
- }
- return ret;
- },
- //类似数组的filter,这方法起得真不好,通常这都是与正则有关的……
- //$.grep( [0,1,2], function(n,i){
- // return n > 0;
- //});
- //[1, 2]
- grep: function( elems, callback, inv ) {
- var ret = [];
- // Go through the array, only saving the items
- // that pass the validator function
- //写法很特别,callback之前的!是为了防止回调函数没有返回值
- //javascript默认没有返回值的函数都返回undefined,这样一搞
- //就变成true,原来返回true的变成false,我们需要负负得正,中和一下
- //于是!=出场了,而inv也是未必存在的,用!强制转换成布尔
- for ( var i = 0, length = elems.length; i < length; i++ )
- if ( !inv != !callback( elems[ i ], i ) )
- ret.push( elems[ i ] );
- return ret;
- },
- //就是数组中的map
- map: function( elems, callback ) {
- var ret = [];
- // Go through the array, translating each of the items to their
- // new value (or values).
- for ( var i = 0, length = elems.length; i < length; i++ ) {
- var value = callback( elems[ i ], i );
- if ( value != null )
- ret[ ret.length ] = value;
- }
- return ret.concat.apply( [], ret );
- }
- });
- // jQuery.browser下面的方法已经被废弃了,这些都是为兼容以前的版本与插件用
- var userAgent = navigator.userAgent.toLowerCase();
- // Figure out what browser is being used
- jQuery.browser = {
- version: (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,‘0‘])[1],
- safari: /webkit/.test( userAgent ),
- opera: /opera/.test( userAgent ),
- msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
- mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent )
- };
- //把以下方法parent,parents,next……添加到jQuery的原型上去,都是一些过滤方法
- jQuery.each({
- parent: function(elem){return elem.parentNode;},
- parents: function(elem){return jQuery.dir(elem,"parentNode");},
- next: function(elem){return jQuery.nth(elem,2,"nextSibling");},
- prev: function(elem){return jQuery.nth(elem,2,"previousSibling");},
- nextAll: function(elem){return jQuery.dir(elem,"nextSibling");},
- prevAll: function(elem){return jQuery.dir(elem,"previousSibling");},
- siblings: function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},
- children: function(elem){return jQuery.sibling(elem.firstChild);},
- contents: function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}
- }, function(name, fn){
- jQuery.fn[ name ] = function( selector ) {//方法体
- var ret = jQuery.map( this, fn );
- if ( selector && typeof selector == "string" )
- ret = jQuery.multiFilter( selector, ret );
- return this.pushStack( jQuery.unique( ret ), name, selector );
- };
- });
- //把以下方法appendTo,prependTo,insertBefore……添加到jQuery的原型上去,
- //利用已有的append,prepend……方法构建
- jQuery.each({
- appendTo: "append",
- prependTo: "prepend",
- insertBefore: "before",
- insertAfter: "after",
- replaceAll: "replaceWith"
- }, function(name, original){
- jQuery.fn[ name ] = function( selector ) {
- var ret = [], insert = jQuery( selector );
- for ( var i = 0, l = insert.length; i < l; i++ ) {
- var elems = (i > 0 ? this.clone(true) : this).get();
- jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
- ret = ret.concat( elems );
- }
- return this.pushStack( ret, name, selector );
- };
- });
- //一些重要常用的静态方法
- jQuery.each({
- removeAttr: function( name ) {
- jQuery.attr( this, name, "" );
- if (this.nodeType == 1)
- this.removeAttribute( name );
- },
- addClass: function( classNames ) {
- jQuery.className.add( this, classNames );
- },
- removeClass: function( classNames ) {
- jQuery.className.remove( this, classNames );
- },
- toggleClass: function( classNames, state ) {
- if( typeof state !== "boolean" )
- state = !jQuery.className.has( this, classNames );
- jQuery.className[ state ? "add" : "remove" ]( this, classNames );
- },
- remove: function( selector ) {
- if ( !selector || jQuery.filter( selector, [ this ] ).length ) {
- // Prevent memory leaks
- jQuery( "*", this ).add([this]).each(function(){
- jQuery.event.remove(this);//★★★★★
- jQuery.removeData(this);
- });
- if (this.parentNode)
- this.parentNode.removeChild( this );
- }
- },
- empty: function() {
- // Remove element nodes and prevent memory leaks
- jQuery(this).children().remove();
- // Remove any remaining nodes
- while ( this.firstChild )
- this.removeChild( this.firstChild );
- }
- }, function(name, fn){
- jQuery.fn[ name ] = function(){
- return this.each( fn, arguments );
- };
- });
- //将带单位的数值去掉单位
- // Helper function used by the dimensions and offset modules
- function num(elem, prop) {
- return elem[0] && parseInt( jQuery.curCSS(elem[0], prop, true), 10 ) || 0;
- }
- </pre>
- <p>接着下来看jQuery的缓存机制,jQuery的性能很大部分依仗于它。</p>
- <pre class="brush:javascript;gutter:false;toolbar:false">
- //@author 司徒正美|RestlessDream|なさみ|cheng http://www.cnblogs.com/rubylouvre/ All rights reserved
- var expando = "jQuery" + now(), uuid = 0, windowData = {};
- jQuery.extend({
- cache: {},
- data: function( elem, name, data ) {
- //坚决不染指window
- elem = elem == window ?
- windowData :
- elem;
- //在elem上设置一个变量
- var id = elem[ expando ];
- // Compute a unique ID for the element
- if ( !id )
- // 同时为id,elem[expando]赋值,值为单一数字
- id = elem[ expando ] = ++uuid;
- // Only generate the data cache if we‘re
- // trying to access or manipulate it
- if ( name && !jQuery.cache[ id ] )
- //在jQuery.cache上开辟一个对象,专门用于储存与那个elem有关的东西
- jQuery.cache[ id ] = {};
- // Prevent overriding the named cache with undefined values
- if ( data !== undefined )//data必须定义
- jQuery.cache[ id ][ name ] = data;
- // Return the named cache data, or the ID for the element
- //根据第二个参数是否存在决定返回的是缓存数据还是element的特别ID
- return name ?
- jQuery.cache[ id ][ name ] :
- id;
- },
- //移除缓存数据
- removeData: function( elem, name ) {
- elem = elem == window ?
- windowData :
- elem;
- var id = elem[ expando ];
- // If we want to remove a specific section of the element‘s data
- if ( name ) {
- if ( jQuery.cache[ id ] ) {
- // Remove the section of cache data
- delete jQuery.cache[ id ][ name ];
- // If we‘ve removed all the data, remove the element‘s cache
- name = "";
- for ( name in jQuery.cache[ id ] )
- break;
- if ( !name )
- jQuery.removeData( elem );
- }
- // Otherwise, we want to remove all of the element‘s data
- } else {
- // Clean up the element expando
- try {
- //IE不能直接用delete去移除,要用removeAttribute
- delete elem[ expando ];
- } catch(e){
- // IE has trouble directly removing the expando
- // but it‘s ok with using removeAttribute
- if ( elem.removeAttribute )
- elem.removeAttribute( expando );
- }
- // Completely remove the data cache
- //用缓存体中把其索引值也移掉
- delete jQuery.cache[ id ];
- }
- },
- //缓存元素的类组数属性
- //可读写
- queue: function( elem, type, data ) {
- if ( elem ){
- type = (type || "fx") + "queue";
- var q = jQuery.data( elem, type );
- if ( !q || jQuery.isArray(data) )
- //q是数组
- q = jQuery.data( elem, type, jQuery.makeArray(data) );
- else if( data )
- q.push( data );
- }
- return q;
- },
- //对元素的类数组缓存进行dequeue(也就是shift)
- dequeue: function( elem, type ){
- var queue = jQuery.queue( elem, type ),
- fn = queue.shift();
- if( !type || type === "fx" )
- fn = queue[0];
- if( fn !== undefined )
- fn.call(elem);
- }
- });
- //让jQuery对象也能获得这种缓存能力
- //都是用上面静态方法实现,最终的缓存体还是jQuery.cache
- jQuery.fn.extend({
- data: function( key, value ){
- var parts = key.split(".");
- parts[1] = parts[1] ? "." + parts[1] : "";
- if ( value === undefined ) {
- var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
- if ( data === undefined && this.length )
- data = jQuery.data( this[0], key );
- return data === undefined && parts[1] ?
- this.data( parts[0] ) :
- data;
- } else
- return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){
- jQuery.data( this, key, value );
- });
- },
- removeData: function( key ){
- return this.each(function(){
- jQuery.removeData( this, key );
- });
- },
- queue: function(type, data){
- if ( typeof type !== "string" ) {
- data = type;
- type = "fx";
- }
- if ( data === undefined )
- return jQuery.queue( this[0], type );
- return this.each(function(){
- var queue = jQuery.queue( this, type, data );
- if( type == "fx" && queue.length == 1 )
- queue[0].call(this);
- });
- },
- dequeue: function(type){
- return this.each(function(){
- jQuery.dequeue( this, type );
- });
- }
- });
- 六
- <p>今天我开始攻略jQuery的心脏,css选择器。不过Sizzle是如此复杂的东西,我发现不能跟着John Resig的思路一行行读下去,因此下面的代码和jQuery的次序是不一样的。</p>
- <p>jQuery 的代码是包含在一个巨大的闭包中,Sizzle又在它里面开辟另一个闭包。它是完全独立于jQuery,jQuery通过find方法来调用 Sizzle。一开始是这几个变量,尤其是那个正则,用于分解我们传入的字符串</p>
- <pre class="brush:javascript;gutter:false;toolbar:false">
- var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|[‘"][^‘"]*[‘"]|[^[\]‘"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,
- done = 0,
- toString = Object.prototype.toString;
- </pre>
- <p>然后我们看其表达式,用于深加工与过滤以及简单的查找:</p>
- <pre class="brush:javascript;gutter:false;toolbar:false">
- //@author 司徒正美|なさみ|cheng http://www.cnblogs.com/rubylouvre/ All rights reserved
- var Expr = Sizzle.selectors = {
- order: [ "ID", "NAME", "TAG" ],
- match: {
- ID: /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
- CLASS: /\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
- NAME: /\[name=[‘"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)[‘"]*\]/,
- ATTR: /\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*([‘"]*)(.*?)\3|)\s*\]/,
- TAG: /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,
- CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
- POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
- PSEUDO: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\(([‘"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
- },
- attrMap: {//一些属性不能直接其HTML名字去取,需要用其在javascript的属性名
- "class": "className",
- "for": "htmlFor"
- },
- attrHandle: {
- href: function(elem){
- return elem.getAttribute("href");
- }
- },
- relative: {
- //相邻选择符
- "+": function(checkSet, part, isXML){
- var isPartStr = typeof part === "string",
- isTag = isPartStr && !/\W/.test(part),
- isPartStrNotTag = isPartStr && !isTag;
- if ( isTag && !isXML ) {
- part = part.toUpperCase();
- }
- for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
- if ( (elem = checkSet[i]) ) {
- while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
- checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
- elem || false :
- elem === part;
- }
- }
- if ( isPartStrNotTag ) {
- Sizzle.filter( part, checkSet, true );
- }
- },
- //亲子选择符
- ">": function(checkSet, part, isXML){
- var isPartStr = typeof part === "string";
- if ( isPartStr && !/\W/.test(part) ) {
- part = isXML ? part : part.toUpperCase();
- for ( var i = 0, l = checkSet.length; i < l; i++ ) {
- var elem = checkSet[i];
- if ( elem ) {
- var parent = elem.parentNode;
- checkSet[i] = parent.nodeName === part ? parent : false;
- }
- }
- } else {
- for ( var i = 0, l = checkSet.length; i < l; i++ ) {
- var elem = checkSet[i];
- if ( elem ) {
- checkSet[i] = isPartStr ?
- elem.parentNode :
- elem.parentNode === part;
- }
- }
- if ( isPartStr ) {
- Sizzle.filter( part, checkSet, true );
- }
- }
- },
- //后代选择符
- "": function(checkSet, part, isXML){
- var doneName = done++, checkFn = dirCheck;
- if ( !part.match(/\W/) ) {
- var nodeCheck = part = isXML ? part : part.toUpperCase();
- checkFn = dirNodeCheck;
- }
- checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
- },
- //兄长选择符
- "~": function(checkSet, part, isXML){
- var doneName = done++, checkFn = dirCheck;
- if ( typeof part === "string" && !part.match(/\W/) ) {
- var nodeCheck = part = isXML ? part : part.toUpperCase();
- checkFn = dirNodeCheck;
- }
- checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
- }
- },
- find: {
- ID: function(match, context, isXML){
- if ( typeof context.getElementById !== "undefined" && !isXML ) {
- var m = context.getElementById(match[1]);
- return m ? [m] : [];//就算只有一个也放进数组
- }
- },
- NAME: function(match, context, isXML){
- if ( typeof context.getElementsByName !== "undefined" ) {
- var ret = [], results = context.getElementsByName(match[1]);
- for ( var i = 0, l = results.length; i < l; i++ ) {
- if ( results[i].getAttribute("name") === match[1] ) {
- ret.push( results[i] );
- }
- }
- return ret.length === 0 ? null : ret;
- }
- },
- TAG: function(match, context){
- return context.getElementsByTagName(match[1]);
- }
- },
- preFilter: {//这里,如果符合的话都返回字符串
- CLASS: function(match, curLoop, inplace, result, not, isXML){
- match = " " + match[1].replace(/\\/g, "") + " ";
- if ( isXML ) {
- return match;
- }
- for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
- if ( elem ) {
- //相当于hasClassName
- if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
- if ( !inplace )
- result.push( elem );
- } else if ( inplace ) {
- curLoop[i] = false;
- }
- }
- }
- return false;
- },
- ID: function(match){
- return match[1].replace(/\\/g, "");
- },
- TAG: function(match, curLoop){
- for ( var i = 0; curLoop[i] === false; i++ ){}
- return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
- },
- CHILD: function(match){
- //把nth(****)里面的表达式都弄成an+b的样子
- if ( match[1] == "nth" ) {
- // parse equations like ‘even‘, ‘odd‘, ‘5‘, ‘2n‘, ‘3n+2‘, ‘4n-1‘, ‘-n+6‘
- var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
- match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
- !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
- // calculate the numbers (first)n+(last) including if they are negative
- match[2] = (test[1] + (test[2] || 1)) - 0;
- match[3] = test[3] - 0;
- }
- // TODO: Move to normal caching system
- match[0] = done++;
- return match;
- },
- ATTR: function(match, curLoop, inplace, result, not, isXML){
- var name = match[1].replace(/\\/g, "");
- if ( !isXML && Expr.attrMap[name] ) {
- match[1] = Expr.attrMap[name];
- }
- if ( match[2] === "~=" ) {
- match[4] = " " + match[4] + " ";
- }
- return match;
- },
- PSEUDO: function(match, curLoop, inplace, result, not){
- if ( match[1] === "not" ) {
- // If we‘re dealing with a complex expression, or a simple one
- if ( match[3].match(chunker).length > 1 || /^\w/.test(match[3]) ) {
- match[3] = Sizzle(match[3], null, null, curLoop);
- } else {
- var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
- if ( !inplace ) {
- result.push.apply( result, ret );
- }
- return false;
- }
- } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
- return true;
- }
- return match;
- },
- POS: function(match){
- match.unshift( true );
- return match;
- }
- },
- filters: {//都是返回布尔值
- enabled: function(elem){
- //不能为隐藏域
- return elem.disabled === false && elem.type !== "hidden";
- },
- disabled: function(elem){
- return elem.disabled === true;
- },
- checked: function(elem){
- return elem.checked === true;
- },
- selected: function(elem){
- // Accessing this property makes selected-by-default
- // options in Safari work properly
- elem.parentNode.selectedIndex;
- return elem.selected === true;
- },
- parent: function(elem){
- //是否是父节点(是,肯定有第一个子节点)
- return !!elem.firstChild;
- },
- empty: function(elem){
- //是否为空,一点节点也没有
- return !elem.firstChild;
- },
- has: function(elem, i, match){
- return !!Sizzle( match[3], elem ).length;
- },
- header: function(elem){
- //是否是h1,h2,h3,h4,h5,h6
- return /h\d/i.test( elem.nodeName );
- },
- text: function(elem){
- //文本域,下面几个相仿,基本上可以归类于属性选择器
- return "text" === elem.type;
- },
- radio: function(elem){
- return "radio" === elem.type;
- },
- checkbox: function(elem){
- return "checkbox" === elem.type;
- },
- file: function(elem){
- return "file" === elem.type;
- },
- password: function(elem){
- return "password" === elem.type;
- },
- submit: function(elem){
- return "submit" === elem.type;
- },
- image: function(elem){
- return "image" === elem.type;
- },
- reset: function(elem){
- return "reset" === elem.type;
- },
- button: function(elem){
- return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
- },
- input: function(elem){
- return /input|select|textarea|button/i.test(elem.nodeName);
- }
- },
- setFilters: {//子元素过滤器
- first: function(elem, i){
- return i === 0;
- },
- last: function(elem, i, match, array){
- return i === array.length - 1;
- },
- even: function(elem, i){
- return i % 2 === 0;
- },
- odd: function(elem, i){
- return i % 2 === 1;
- },
- lt: function(elem, i, match){
- return i < match[3] - 0;
- },
- gt: function(elem, i, match){
- return i > match[3] - 0;
- },
- nth: function(elem, i, match){
- return match[3] - 0 == i;
- },
- eq: function(elem, i, match){
- return match[3] - 0 == i;
- }
- },
- filter: {
- PSEUDO: function(elem, match, i, array){
- var name = match[1], filter = Expr.filters[ name ];
- if ( filter ) {
- return filter( elem, i, match, array );
- } else if ( name === "contains" ) {
- return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
- } else if ( name === "not" ) {
- var not = match[3];
- for ( var i = 0, l = not.length; i < l; i++ ) {
- if ( not[i] === elem ) {
- return false;
- }
- }
- return true;
- }
- },
- CHILD: function(elem, match){
- var type = match[1], node = elem;
- switch (type) {
- case ‘only‘:
- case ‘first‘:
- while (node = node.previousSibling) {
- if ( node.nodeType === 1 ) return false;
- }
- if ( type == ‘first‘) return true;
- node = elem;
- case ‘last‘:
- while (node = node.nextSibling) {
- if ( node.nodeType === 1 ) return false;
- }
- return true;
- case ‘nth‘:
- var first = match[2], last = match[3];
- if ( first == 1 && last == 0 ) {
- return true;
- }
- var doneName = match[0],
- parent = elem.parentNode;
- if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
- var count = 0;
- for ( node = parent.firstChild; node; node = node.nextSibling ) {
- if ( node.nodeType === 1 ) {
- node.nodeIndex = ++count;//添加一个私有属性
- }
- }
- parent.sizcache = doneName;
- }
- var diff = elem.nodeIndex - last;
- if ( first == 0 ) {
- return diff == 0;//判断是否为第一个子元素
- } else {
- return ( diff % first == 0 && diff / first >= 0 );
- }
- }
- },
- ID: function(elem, match){
- return elem.nodeType === 1 && elem.getAttribute("id") === match;
- },
- TAG: function(elem, match){
- return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
- },
- CLASS: function(elem, match){
- return (" " + (elem.className || elem.getAttribute("class")) + " ")
- .indexOf( match ) > -1;
- },
- ATTR: function(elem, match){
- var name = match[1],
- result = Expr.attrHandle[ name ] ?
- Expr.attrHandle[ name ]( elem ) :
- elem[ name ] != null ?
- elem[ name ] :
- elem.getAttribute( name ),
- value = result + "",
- type = match[2],
- check = match[4];
- return result == null ?
- type === "!=" :
- type === "=" ?
- value === check :
- type === "*=" ?
- value.indexOf(check) >= 0 :
- type === "~=" ?
- (" " + value + " ").indexOf(check) >= 0 :
- !check ?
- value && result !== false :
- type === "!=" ?
- value != check :
- type === "^=" ?
- value.indexOf(check) === 0 :
- type === "$=" ?
- value.substr(value.length - check.length) === check :
- type === "|=" ?
- value === check || value.substr(0, check.length + 1) === check + "-" :
- false;
- },
- POS: function(elem, match, i, array){
- var name = match[2], filter = Expr.setFilters[ name ];
- if ( filter ) {
- return filter( elem, i, match, array );
- }
- }
- }
- };
- var origPOS = Expr.match.POS;
- </pre>
- <div><img src="http://images.cnblogs.com/cnblogs_com/rubylouvre/205314/o_Sizzle.gif"></div>
- <p> 但上图没有完全显现Sizzle复杂的工作机制,它是从左到右工作,加工了一个字符串,查找,然后过滤非元素节点,再跟据其属性或内容或在父元素的顺序过 滤,然后到下一个字符串,这时搜索起点就是上次的结果数组的元素节点,想象一下草根的样子吧。在许多情况下,选择器都是靠工作 的,element.getElementsByTagName(*),获得其一元素的所有子孙,因此Expr中的过滤器特别多。为了过快查找速度,如有 些浏览器已经实现了getElementsByClassName,jQuery也设法把它们利用起来。</p>
- <pre class="brush:javascript;gutter:false;toolbar:false">
- for ( var type in Expr.match ) {
- //重写Expr.match中的正则,利用负向零宽断言让其更加严谨
- Expr.match[ type ] = RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
- }
- </pre>
- <p>接着下来我们还是未到时候看上面的主程序,继续看它的辅助方法。</p>
- <pre class="brush:javascript;gutter:false;toolbar:false">
- //把NodeList HTMLCollection转换成纯数组,如果有第二参数(上次查找的结果),则把它们加入到结果集中
- var makeArray = function(array, results) {
- array = Array.prototype.slice.call( array );
- if ( results ) {
- results.push.apply( results, array );
- return results;
- }
- return array;
- };
- try {
- //基本上是用于测试IE的,IE的NodeList HTMLCollection不支持用数组的slice转换为数组
- Array.prototype.slice.call( document.documentElement.childNodes );
- //这时就要重载makeArray,一个个元素搬入一个空数组中了
- } catch(e){
- makeArray = function(array, results) {
- var ret = results || [];
- if ( toString.call(array) === "[object Array]" ) {
- Array.prototype.push.apply( ret, array );
- } else {
- if ( typeof array.length === "number" ) {
- for ( var i = 0, l = array.length; i < l; i++ ) {
- ret.push( array[i] );
- }
- } else {
- for ( var i = 0; array[i]; i++ ) {
- ret.push( array[i] );
- }
- }
- }
- return ret;
- };
- }
- 七
- <p>在Sizzle中有许多有用的辅助方法,我们继续一个个看。其中涉及许多BUG的修正以及一些很少见的API。</p>
- <pre class="brush:javascript;gutter:false;toolbar:false">
- //@author 司徒正美|なさみ|cheng http://www.cnblogs.com/rubylouvre/ All rights reserved
- var sortOrder;//比较两个元素在页面上的顺序,返回正数,0,负数
- //如果支持compareDocumentPosition方法,新锐的标准浏览器都支持
- //我在《javascript contains方法》一文中有详细介绍
- //http://www.cnblogs.com/rubylouvre/archive/2009/10/14/1583523.html
- if ( document.documentElement.compareDocumentPosition ) {
- sortOrder = function( a, b ) {
- //节点a 在节点b 之前,
- var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
- if ( ret === 0 ) {
- hasDuplicate = true;
- }
- return ret;
- };
- //用于IE
- //sourceIndex是指元素在NodeList中的位置
- } else if ( "sourceIndex" in document.documentElement ) {
- sortOrder = function( a, b ) {
- var ret = a.sourceIndex - b.sourceIndex;
- if ( ret === 0 ) {
- hasDuplicate = true;
- }
- return ret;
- };
- //用于旧式的标准游览器
- } else if ( document.createRange ) {
- sortOrder = function( a, b ) {
- var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
- aRange.selectNode(a);
- aRange.collapse(true);
- bRange.selectNode(b);
- bRange.collapse(true);
- //比较两个selection的位置
- //https://developer.mozilla.org/en/DOM/range.compareBoundaryPoints
- var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
- if ( ret === 0 ) {
- hasDuplicate = true;
- }
- return ret;
- };
- }
- </pre>
- <p>比较元素位置在IE还可以用uniqueNumber,都是自上至下分配数字。</p>
- <p>下面对getElementById,getElementsByTagName,getElementsByClassName, querySelectorAll 进行调整。</p>
- <pre class="brush:javascript;gutter:false;toolbar:false">
- //在getElementById(XXX)在IE中有bug,它会找第一个属性name或id等于XXX的元素,
- //尤其是在表单元素中,它们通常都带有name属性
- (function(){
- // We‘re going to inject a fake input element with a specified name
- var form = document.createElement("form"),
- id = "script" + (new Date).getTime();
- form.innerHTML = "<input name=‘" + id + "‘/>";
- // Inject it into the root element, check its status, and remove it quickly
- var root = document.documentElement;
- root.insertBefore( form, root.firstChild );
- // The workaround has to do additional checks after a getElementById
- // Which slows things down for other browsers (hence the branching)
- if ( !!document.getElementById( id ) ) {
- //重载一下Expr.find.ID
- Expr.find.ID = function(match, context, isXML){
- if ( typeof context.getElementById !== "undefined" && !isXML ) {
- var m = context.getElementById(match[1]);
- //确定此元素是否显式为id赋值
- return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" &&
- m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
- }
- };
- Expr.filter.ID = function(elem, match){
- //确定此元素是否显式为id赋值
- var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
- return elem.nodeType === 1 && node && node.nodeValue === match;
- };
- }
- root.removeChild( form );
- })();
- (function(){
- // Check to see if the browser returns only elements
- // when doing getElementsByTagName("*")
- // Create a fake element
- var div = document.createElement("div");
- div.appendChild( document.createComment("") );
- // Make sure no comments are found
- if ( div.getElementsByTagName("*").length > 0 ) {
- //重载Expr.find.TAG
- Expr.find.TAG = function(match, context){
- var results = context.getElementsByTagName(match[1]);
- // Filter out possible comments
- //返回其所有元素节点后代,组成纯数组
- if ( match[1] === "*" ) {
- var tmp = [];
- for ( var i = 0; results[i]; i++ ) {
- if ( results[i].nodeType === 1 ) {
- tmp.push( results[i] );
- }
- }
- results = tmp;
- }
- return results;
- };
- }
- // Check to see if an attribute returns normalized href attributes
- //处理href属性,如果第二个参数,IE返回的是绝对路径
- div.innerHTML = "<a href=‘#‘></a>";
- if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
- div.firstChild.getAttribute("href") !== "#" ) {
- Expr.attrHandle.href = function(elem){
- return elem.getAttribute("href", 2);
- };
- }
- })();
- if ( document.querySelectorAll ) (function(){
- //创建一个元素片段<div><p class=‘TEST‘></p></div>
- //用querySelectorAll看看能否正确找到这个p元素
- var oldSizzle = Sizzle, div = document.createElement("div");
- div.innerHTML = "<p class=‘TEST‘></p>";
- // Safari can‘t handle uppercase or unicode characters when
- // in quirks mode.
- if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
- return;
- }
- //如果能,就用querySelectorAll重载整个Sizzle引擎,效率最高!!!
- Sizzle = function(query, context, extra, seed){
- context = context || document;
- // Only use querySelectorAll on non-XML documents
- // (ID selectors don‘t work in non-HTML documents)
- if ( !seed && context.nodeType === 9 && !isXML(context) ) {
- try {
- return makeArray( context.querySelectorAll(query), extra );
- } catch(e){}
- }
- return oldSizzle(query, context, extra, seed);
- };
- Sizzle.find = oldSizzle.find;
- Sizzle.filter = oldSizzle.filter;
- Sizzle.selectors = oldSizzle.selectors;
- Sizzle.matches = oldSizzle.matches;
- })();
- if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
- // 创建一个元素片段<div><div class=‘test e‘>< /div><div class=‘test‘></div></div>
- //用getElementsByClassName看看能否正确找到这两个div元素
- var div = document.createElement("div");
- div.innerHTML = "<div class=‘test e‘></div><div class=‘test‘></div>";
- // Opera can‘t find a second classname (in 9.6)
- if ( div.getElementsByClassName("e").length === 0 )
- return;
- // Safari caches class attributes, doesn‘t catch changes (in 3.2)
- div.lastChild.className = "e";
- if ( div.getElementsByClassName("e").length === 1 )
- return;
- //重新调整与CLASS有关的逻辑
- Expr.order.splice(1, 0, "CLASS");
- Expr.find.CLASS = function(match, context, isXML) {
- if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
- return context.getElementsByClassName(match[1]);
- }
- };
- })();
- </pre>
- <pre class="brush:javascript;gutter:false;toolbar:false">
- //这东西用于后代选择器与兄长选择器,取得某范围中所有元素,并且防止重复取得
- function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
- var sibDir = dir == "previousSibling" && !isXML;
- //checkSet为元素集合,doneName为数字
- for ( var i = 0, l = checkSet.length; i < l; i++ ) {
- var elem = checkSet[i];
- if ( elem ) {
- if ( sibDir && elem.nodeType === 1 ){
- elem.sizcache = doneName;//设置一标记,以后有与它值相等的不重复取
- elem.sizset = i;
- }
- elem = elem[dir];
- var match = false;
- while ( elem ) {
- if ( elem.sizcache === doneName ) {//比较是否相等
- match = checkSet[elem.sizset];
- break;
- }
- if ( elem.nodeType === 1 && !isXML ){
- elem.sizcache = doneName;
- elem.sizset = i;
- }
- if ( elem.nodeName === cur ) {
- match = elem;
- break;
- }
- elem = elem[dir];
- }
- checkSet[i] = match;
- }
- }
- }
- //和上面功能差不多,不知是否出于兼容以前版本的需要……
- function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
- var sibDir = dir == "previousSibling" && !isXML;
- for ( var i = 0, l = checkSet.length; i < l; i++ ) {
- var elem = checkSet[i];
- if ( elem ) {
- if ( sibDir && elem.nodeType === 1 ) {
- elem.sizcache = doneName;
- elem.sizset = i;
- }
- elem = elem[dir];
- var match = false;
- while ( elem ) {
- if ( elem.sizcache === doneName ) {
- match = checkSet[elem.sizset];
- break;
- }
- if ( elem.nodeType === 1 ) {
- if ( !isXML ) {
- elem.sizcache = doneName;
- elem.sizset = i;
- }
- if ( typeof cur !== "string" ) {
- if ( elem === cur ) {
- match = true;
- break;
- }
- } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
- match = elem;
- break;
- }
- }
- elem = elem[dir];
- }
- checkSet[i] = match;
- }
- }
- }
- </pre>
- <pre class="brush:javascript;gutter:false;toolbar:false">
- //判断一个元素是否包含另一个元素
- //http://www.cnblogs.com/rubylouvre/archive/2009/10/14/1583523.html
- var contains = document.compareDocumentPosition ? function(a, b){
- return a.compareDocumentPosition(b) & 16;
- } : function(a, b){
- return a !== b && (a.contains ? a.contains(b) : true);
- };
- //判断是否为XML
- var isXML = function(elem){
- return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
- !!elem.ownerDocument && isXML( elem.ownerDocument );
- };
- //主要是处理结构伪类中的子元素过滤器
- var posProcess = function(selector, context){
- var tmpSet = [], later = "", match,
- root = context.nodeType ? [context] : context;
- // Position selectors must be done after the filter
- // And so must :not(positional) so we move all PSEUDOs to the end
- while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
- later += match[0];
- selector = selector.replace( Expr.match.PSEUDO, "" );
- }
- //如果不是在亲子中选择,就是在它的所有后代中选择“*”
- selector = Expr.relative[selector] ? selector + "*" : selector;
- //回调Sizzle
- for ( var i = 0, l = root.length; i < l; i++ ) {
- Sizzle( selector, root[i], tmpSet );
- }
- return Sizzle.filter( later, tmpSet );
- };
- 八
- <p>今天把jQuery的Sizzle选择器引擎讲完。最后给出其大体的工作流程。这东西非常复杂,不要妄图看一遍就明白了。无论看懂与否,多看点源码,还是有裨益的。至少在处理循环结构上有收获吧。</p>
- <pre class="brush:javascript;gutter:false;toolbar:false">
- //@author 司徒正美|なさみ|cheng http://www.cnblogs.com/rubylouvre/ All rights reserved
- // EXPOSE
- jQuery.find = Sizzle;
- jQuery.filter = Sizzle.filter;
- jQuery.expr = Sizzle.selectors;
- //以:开头许多都是自定义伪类
- jQuery.expr[":"] = jQuery.expr.filters;
- //css属性display引起的元素不可见
- Sizzle.selectors.filters.hidden = function(elem){
- return elem.offsetWidth === 0 || elem.offsetHeight === 0;
- };
- //css属性display引起的元素不可见
- Sizzle.selectors.filters.visible = function(elem){
- return elem.offsetWidth > 0 || elem.offsetHeight > 0;
- };
- //是否在运动中
- Sizzle.selectors.filters.animated = function(elem){
- return jQuery.grep(jQuery.timers, function(fn){
- return elem === fn.elem;
- }).length;
- };
- //重载jQuery.multiFilter
- jQuery.multiFilter = function( expr, elems, not ) {
- if ( not ) {
- expr = ":not(" + expr + ")";
- }
- return Sizzle.matches(expr, elems);
- };
- //把路径上的元素放到结果上,dir为parentNode,previousSibling,nextSilbing
- jQuery.dir = function( elem, dir ){
- var matched = [], cur = elem[dir];
- while ( cur && cur != document ) {
- if ( cur.nodeType == 1 )
- matched.push( cur );
- cur = cur[dir];
- }
- return matched;
- };
- //在内部调用result好像都为2,dir为previousSibling,nextSilbing
- //用于子元素过滤
- jQuery.nth = function(cur, result, dir, elem){
- result = result || 1;
- var num = 0;
- //如果cur为undefined中止循环
- for ( ; cur; cur = cur[dir] )
- if ( cur.nodeType == 1 && ++num == result )
- break;
- return cur;
- };
- //查找不等于elem的兄弟元素节点
- jQuery.sibling = function(n, elem){
- var r = [];
- for ( ; n; n = n.nextSibling ) {
- if ( n.nodeType == 1 && n != elem )
- r.push( n );
- }
- return r;
- };
- return;
- window.Sizzle = Sizzle;
- </pre>
- <p>好了,回头看Sizzle的主程序部分:</p>
- <pre class="brush:javascript;gutter:false;toolbar:false">
- Sizzle.find = function(expr, context, isXML){
- var set, match;
- if ( !expr ) {//如果不是字符串表达式则返回空数组
- return [];
- }
- for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
- var type = Expr.order[i], match;//按照ID NAME TAG的优先级顺序执行
- //这里可以想象一下
- //match = "#aaa".exec( /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/)
- //然后检测match是否为空数组,空数组相当于false
- if ( (match = Expr.match[ type ].exec( expr )) ) {
- //ID的正则 /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/
- var left = RegExp.leftContext
- //如果不是一步到位,是复杂的表达式,需要多次查找与筛选
- if ( left.substr( left.length - 1 ) !== "\\" ) {
- //把换行符去掉,得到正常的字段
- //如"#id12\
- //34"
- //去掉后,就得到"#id1234"
- match[1] = (match[1] || "").replace(/\\/g, "");
- set = Expr.find[ type ]( match, context, isXML );
- if ( set != null ) {
- //移除相应部分的表达,
- // 如#aaa ee,得到ID对应的元素后,把#aaa去掉,
- //然后用Expr的表达式来匹配剩下的部分
- expr = expr.replace( Expr.match[ type ], "" );
- break;
- }
- }
- }
- }
- if ( !set ) {
- //返回所有后代
- set = context.getElementsByTagName("*");
- }
- return {//返回一个对象
- set: set,
- expr: expr
- };
- };
- </pre>
- <pre class="brush:javascript;gutter:false;toolbar:false">
- Sizzle.filter = function(expr, set, inplace, not){
- var old = expr, result = [], curLoop = set, match, anyFound,
- isXMLFilter = set && set[0] && isXML(set[0]);
- while ( expr && set.length ) {
- for ( var type in Expr.filter ) {
- //这是Expr.filter中的键值对
- //PSEUDO: function(elem, match, i, array){},
- //CHILD: function(elem, match){},
- //ID: function(elem, match){},
- //TAG: function(elem, match){},
- //CLASS: function(elem, match){},
- //ATTR: function(elem, match){},
- //POS: function(elem, match, i, array){}
- if ( (match = Expr.match[ type ].exec( expr )) != null ) {//match为数组
- var filter = Expr.filter[ type ], found, item;//filter这函数
- anyFound = false;
- if ( curLoop == result ) {//如果结果集为空数组,就让result = [];
- result = [];
- }
- if ( Expr.preFilter[ type ] ) {
- //这是Expr.preFilter中的键值对
- //CLASS: function(match, curLoop, inplace, result, not, isXML){},
- //ID: function(match){},
- //TAG: function(match, curLoop){},
- //CHILD: function(match){ },
- //ATTR: function(match, curLoop, inplace, result, not, isXML){},
- //PSEUDO: function(match, curLoop, inplace, result, not){ },
- //POS: function(match){}
- //preFilter与filter的功能不同,preFilter对字符串进行调整,好让选择器能找到元素
- //filter对查找到的元素或元素数组进行筛选
- match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
- if ( !match ) {//如果返回的是false
- anyFound = found = true;//就把anyFound与found标记为true
- } else if ( match === true ) {
- continue;
- }
- }
- if ( match ) {
- for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
- if ( item ) {
- //检测元素是否符合要求
- found = filter( item, match, i, curLoop );
- var pass = not ^ !!found;
- if ( inplace && found != null ) {
- if ( pass ) {
- anyFound = true;
- } else {
- curLoop[i] = false;
- }
- } else if ( pass ) {
- result.push( item );//符合要求就放到结果数组中
- anyFound = true;
- }
- }
- }
- }
- if ( found !== undefined ) {
- if ( !inplace ) {
- curLoop = result;//结果数组将作为一下次要遍历的元素集合返回
- }
- //移除用户输入字符串已查找了的那一部分表达式
- expr = expr.replace( Expr.match[ type ], "" );
- if ( !anyFound ) {
- return [];
- }
- break;
- }
- }
- }
- // Improper expression
- if ( expr == old ) {
- if ( anyFound == null ) {
- throw "Syntax error, unrecognized expression: " + expr;
- } else {
- break;
- }
- }
- old = expr;
- }
- return curLoop;
- };
- </pre>
- <p>主程序:</p>
- <pre class="brush:javascript;gutter:false;toolbar:false">
- var Sizzle = function(selector, context, results, seed) {
- results = results || [];
- context = context || document;
- if ( context.nodeType !== 1 && context.nodeType !== 9 )
- return [];//context必须为DOM元素或document,要不返回空数组
- if ( !selector || typeof selector !== "string" ) {
- return results;//selector必须存在并且为字符串,否则返回上次循环的结果集
- }
- var parts = [], m, set, checkSet, check, mode, extra, prune = true;
- // Reset the position of the chunker regexp (start from head)
- chunker.lastIndex = 0;
- while ( (m = chunker.exec(selector)) !== null ) {
- parts.push( m[1] );
- if ( m[2] ) {
- extra = RegExp.rightContext;//匹配内容的右边归入extra
- break;
- }
- }
- //POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
- if ( parts.length > 1 && origPOS.exec( selector ) ) {
- //处理E F E > F E + F E ~ F
- if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
- //这里的parts[0]肯定不是“”,亦即不会是后代选择器
- set = posProcess( parts[0] + parts[1], context );
- } else {
- set = Expr.relative[ parts[0] ] ?
- [ context ] :
- Sizzle( parts.shift(), context );
- while ( parts.length ) {
- selector = parts.shift()
- if ( Expr.relative[ selector ] )
- selector += parts.shift();
- set = posProcess( selector, set );
- }
- }
- } else {
- var ret = seed ?
- {
- expr: parts.pop(),
- set: makeArray(seed)
- } :
- Sizzle.find( parts.pop(), parts.length === 1 && context.parentNode ? context.parentNode : context, isXML(context) );
- set = Sizzle.filter( ret.expr, ret.set );
- if ( parts.length > 0 ) {
- checkSet = makeArray(set);
- } else {
- prune = false;
- }
- while ( parts.length ) {//倒序的while循环比for循环快
- var cur = parts.pop(), pop = cur;
- if ( !Expr.relative[ cur ] ) {
- cur = "";
- } else {
- pop = parts.pop();
- }
- if ( pop == null ) {
- pop = context;
- }
- Expr.relative[ cur ]( checkSet, pop, isXML(context) );
- }
- }
- if ( !checkSet ) {
- checkSet = set;
- }
- if ( !checkSet ) {
- throw "Syntax error, unrecognized expression: " + (cur || selector);
- }
- //数组化NodeList,并加入结果集中
- if ( toString.call(checkSet) === "[object Array]" ) {
- if ( !prune ) {
- results.push.apply( results, checkSet );
- } else if ( context.nodeType === 1 ) {
- for ( var i = 0; checkSet[i] != null; i++ ) {
- if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
- results.push( set[i] );
- }
- }
- } else {
- for ( var i = 0; checkSet[i] != null; i++ ) {
- if ( checkSet[i] && checkSet[i].nodeType === 1 ) {//确保是元素节点
- results.push( set[i] );
- }
- }
- }
- } else {
- makeArray( checkSet, results );
- }
- if ( extra ) {
- Sizzle( extra, context, results, seed );
- if ( sortOrder ) {
- hasDuplicate = false;
- results.sort(sortOrder);//重排结果集中的DOM元素,按照原来在网页先后顺序排列
- if ( hasDuplicate ) {
- for ( var i = 1; i < results.length; i++ ) {//确保没有重复的DOM元素,方法比较垃圾
- if ( results[i] === results[i-1] ) {
- results.splice(i--, 1);
- }
- }
- }
- }
- }
- return results;
- };
- </pre>
- <p>最后重新说一下其逻辑:</p>
- <ol>
- <li>首先用一个叫chunker的强大正则,把诸如 var str = " #div , h1#id\<br />
- 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>
- <li> 接着对上下文的内容进行判断,确保其为DOM元素或document,否则返回空数组。然后判断selector是否为字符串,由于Sizzle会不断递 归调用,selector会越来越短的,直到为零。这些越来越短的selector其实也是第一次chunker 分解的结果之一。不过它们都有可能g再 遭分解。每一次循环,这些分解了的字符串都会经过筛选(非空字符),放入parts数组中。</li>
- <li> 这些selector最先会判断一下,是否为亲子兄长相邻后代等关系选择器。由于第一次chunker把大部分空白消灭了,造成了一个不幸的结果,把后代 选择器也消灭了。因此必须补上后代选择器。详见后面posProcess的“selector + "*"”操作。</li>
- <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>
- <li> 根据id,name与tag找到这些元素后,下一个循环就是找它们的子元素或后代元素了,所以Sizzle才会急冲冲地修复后代选择器的问题。至于筛 选,Expr有大量的方法来进行。最后是重新排序与去除重复选中的元素,以结果集返回。</li>
- </ol>
时间: 2024-10-25 06:23:32