首页 > 代码库 > Sizzle一步步实现所有功能(层级选择)

Sizzle一步步实现所有功能(层级选择)

第二步:实现Sizzle("el,el,el..."),Sizzle("el > el"),Sizzle("el el"),Sizzle("el + el"),Sizzle("el ~ el")

  1 (function( window ){  2         3 var arr = [];  4 var select ;  5 var Expr;  6 var push = arr.push;  7 // http://www.w3.org/TR/css3-selectors/#whitespace  8 // 各种空白待穿正则字符串  9 var whitespace = "[\\x20\\t\\r\\n\\f]"; 10 // 带空格选择器正则,记忆无空格选择器 11 // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier 12 var    identifier = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+"; 13 // 属性选择器: http://www.w3.org/TR/selectors/#attribute-selectors 14 var    attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + 15         // Operator (capture 2) 16         "*([*^$|!~]?=)" + whitespace + 17         // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" 18         "*(?:‘((?:\\\\.|[^\\\\‘])*)‘|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + 19         "*\\]"; 20 var rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ); 21 // 快速选择器正则 ID 或者 TAG(包括*) 或者 CLASS 选择器 22 var rquickExpr = /^(?:#([\w-]+)|(\w+|\*)|\.([\w-]+))$/; 23 // 连接符号 24 var rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ); 25 // 层级符号正则‘>‘,‘ ‘,‘+‘,‘~‘ 26 var rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ); 27 var matchExpr = { 28         "ID": new RegExp( "^#(" + identifier + ")" ), 29         "CLASS": new RegExp( "^\\.(" + identifier + ")" ), 30         "TAG": new RegExp( "^(" + identifier + "|[*])" ), 31 }; 32 // 浏览器代码正则 33 var rnative = /^[^{]+\{\s*\[native \w/; 34 // token缓存 35 var tokenCache = createCache(); 36 // 编译缓存 37 var compilerCache = createCache(); 38 // 入口 39 function Sizzle( selector ){ 40     // 清除空格 41     selector = selector.replace( rtrim, "$1" ) 42     var results = []; 43     var match; 44     var matcher; 45     var elem; 46     var m; 47     var context = document; 48      49     // 是否为最简选择器 50     if( match = rquickExpr.exec( selector )){ 51         // Sizzle(‘#ID) 52         if ( (m = match[1]) ) { 53             elem = context.getElementById( m ); 54             if( elem ){ 55                 results.push( elem ); 56             } 57             return results; 58              59         // Sizzle("TAG") 60         }else if( (m = match[2]) ){ 61             push.apply( results, context.getElementsByTagName( selector ) ); 62             return results; 63          64         // Sizzle(".CLASS")     65         }else if( (m = match[3]) ){ 66             // 支持getElementsByClassName 67             if( support.getElementsByClassName ){ 68                 push.apply( results, context.getElementsByClassName( m ) ); 69                 return results; 70             } 71         } 72     } 73     // 复杂选择调到select 74     return select( selector, context, results); 75 } 76 // 创建缓存函数 77 function createCache() { 78     var keys = []; 79  80     function cache( key, value ) { 81         // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) 82         if ( keys.push( key + " " ) > 10 ) { 83             // Only keep the most recent entries 84             delete cache[ keys.shift() ]; 85         } 86         return (cache[ key + " " ] = value); 87     } 88     return cache; 89 } 90 // 错误函数 91 Sizzle.error = function( msg ) { 92     throw new Error( "Syntax error, unrecognized expression: " + msg ); 93 }; 94 // 版本支持变量的对外访问入口 95 var support = Sizzle.support = {}; 96  97 // 判断是否支持getElementsByClassName 98 // 支持: IE<9 99 support.getElementsByClassName = rnative.test( document.getElementsByClassName );100 // 表达式对象101 // 存放各类相对位置,各种查询函数,各种过滤函数等。102 Expr = {103     relative: {104         ">": { dir: "parentNode", first: true },105         " ": { dir: "parentNode" },106         "+": { dir: "previousSibling", first: true },107         "~": { dir: "previousSibling" }108     },109     filter: {110         "TAG": function( nodeNameSelector ) {111             var nodeName = nodeNameSelector.toLowerCase();112             return nodeNameSelector === "*" ?113                 function() { return true; } :114                 function( elem ) {115                     return elem.nodeName.toLowerCase() === nodeName;116                 };117         },118         "CLASS": function( className ) {119             var className = className.toLowerCase();120             return function( elem ) {121                     return elem.className.toLowerCase() === className;122             };123         }124     },125     find: {126         "TAG": function( tag, context ) {127             return context.getElementsByTagName( tag );128         },129         "CLASS": support.getElementsByClassName&&function( tag, context ) {130             return context.getElementsByClassName( tag );131         },132     },133 }134 // tokenize函数135 // 将选择器字符串转化为方便使用的数组对象形式136 tokenize = Sizzle.tokenize = function( selector, parseOnly ) {137     var cached = tokenCache[ selector + " " ];138     139     // cached.slice生成新的数组,对其修改不会修改其引用缓存140     if ( cached ) {141         return cached.slice( 0 );142     }143     // 循环条件144     var soFar = selector;145     // 结果数组146     var groups = [];147     // 匹配参数148     var matched;149     // 一个独立的tokens150     var tokens;151     // 辅助变量152     var match;153 154     while ( soFar ) {155 156         //首次默认创建一个tokens157         //之后每碰到一个逗号新增一个新的tokens158         if ( !matched || (match = rcomma.exec( soFar )) ) {159             if ( match ) {160                 // Don‘t consume trailing commas as valid161                 soFar = soFar.slice( match[0].length ) || soFar;162             }163             groups.push( (tokens = []) );164         }165         166         matched = false;167 168         // 关系token169         if ( (match = rcombinators.exec( soFar )) ) {170             matched = match.shift();171             tokens.push({172                 value: matched,173                 // Cast descendant combinators to space174                 type: match[0].replace( rtrim, " " )175             });176             soFar = soFar.slice( matched.length );177         }178 179         // TAG,CLASS,ID token180         for ( type in Expr.filter ) {181             if ( match = matchExpr[ type ].exec( soFar ) ) {182                 matched = match.shift();183                 tokens.push({184                     value: matched,185                     type: type,186                     matches: match187                 });188                 soFar = soFar.slice( matched.length );189             }190         }191         // 一次循环到这里三个条件都不符合没有匹配结果时,跳出。192         if ( !matched ) {193             break;194         }195     }196 197     // 意外跳出,soFar存在,报错。198     return soFar ?199             Sizzle.error( selector ) :200             // 缓存后转成新数组返回(预防修改缓存内容)201             tokenCache( selector, groups ).slice( 0 );202 };203 // 将tokens转化为selector字符串形式。204 function toSelector( tokens ) {205     var i = 0,206         len = tokens.length,207         selector = "";208     for ( ; i < len; i++ ) {209         selector += tokens[i].value;210     }211     return selector;212 }213 // !addCombinator214 // 增加关系处理函数215 // 返回关系函数,主要功能是,遍历种子节点的关系节点。216 // 比如li>a,传入无数个种子节点a,a.parentNode,再执行matcher,matcher里再判断这个父亲节点是不是li217 function addCombinator( matcher, combinator ) {218     var dir = combinator.dir;219     return combinator.first ?220         function( elem, context ) {221             while( (elem = elem[ dir ]) ){222                 if ( elem.nodeType === 1 ) {223                     return matcher( elem, context );224                 }225             }226         }:227         function( elem, context ) {228             while ( (elem = elem[ dir ]) ) {229                 if ( elem.nodeType === 1 ) {230                     if(matcher( elem, context )) {231                         return true;232                     }233                 }234             }235             return false; 236         }237 }238 239 // !elementMatcher240 // 生成matchers遍历器241 // matchers数组存放我要过滤的函数,这个函数遍历所有过滤函数,一个不符合就返回false。242 function elementMatcher( matchers ) {243     return function( elem, context ) {244         var i = matchers.length;245         while ( i-- ) {246             if ( !matchers[i]( elem, context ) ) {247                 return false;248             }249         }250         return true;251     };252 }253 // !matcherFromTokens254 // 根据tokens,生成过滤一组函数matchers,供elementMatcher使用255 // 返回的是一个执行所有过滤函数的函数256 function matcherFromTokens( tokens ){257     var matchers = [];258     var matcher;259     var i = 0;260     var len = tokens.length;261     for ( ; i < len; i++ ) {262         if ( (matcher = Expr.relative[ tokens[i].type ]) ) {263             matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];264         } else {265             matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );266             matchers.push( matcher );267         }268     }269     return elementMatcher( matchers );270 }271 // !matcherFromGroupMatchers272 // 返回超级匹配器,273 function matcherFromGroupMatchers( elementMatchers ){274     // !!最重要superMatcher,也是最核心的函数,其它的函数为它服务。275     // 获取种子元素,遍历所有种子元素。276     // 遍历elementMatchers277     // 符合的推入结果数组278     // 一个选择器(逗号隔开的)生成一个elementMatcher,elementMatchers是存放所有elementMatcher的数组279     var superMatcher = function( seed, context, results) {280         var elems = seed || Expr.find["TAG"]( "*", document );281         var len = elems.length;282         var i = 0;283         for ( ; i !== len && (elem = elems[i]) != null; i++ ) {284             j = 0;285             while ( (matcher = elementMatchers[j++]) ) {286                 if ( matcher( elem, context) ) {287                     results.push( elem );288                     break;289                 }290             }291         }292     }293     return superMatcher;294 }295 // compile296 // 最初的编译器,存放elementMatchers,缓存超级匹配函数并返回297 compile = Sizzle.compile = function( selector, match ) {298     var i;299     var elementMatchers = [];300     var    cached = compilerCache[ selector + " "];301     if ( !cached ) {302         i = match.length;303         while ( i-- ) {304             elementMatchers.push( matcherFromTokens( match[i] ) );305         }306         cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers ));307     }308     return cached;309 }310 // select311 // 兼容的自写的选择器312 select = Sizzle.select = function( selector, context, results){313     var token;314     var seed;315     var tokens;316     var find;317     var match = tokenize( selector )318     if ( match.length === 1 ) {319         // tokens320         var tokens = match[0].slice( 0 );321         // 如果tokens的首项是ID,将其设置为上下文322         if ( (token = tokens[0]).type === ‘ID‘ ){    323             context = document.getElementById(token.matches[0]);324             selector = selector.slice( tokens.shift().value.length );325         }326         // 生成种子seed327         // 如"div ul li",所谓种子就是所有的li328         // 后面编译函数需要过滤出符合祖先是ul,ul的祖先是div的节点329         i = tokens.length;330         while ( i-- ){331             token = tokens[i];332             if ( Expr.relative[ (type = token.type) ] ) {333                 break;334             }335             if((find =  Expr.find[ type ]))336                 if( seed = find( token.matches[0],context ) ) {337                     tokens.splice( i, 1 );338                     selector = toSelector( tokens )339                     break;340                 }341         } 342     };343     // 根据selector,tokens(match防止对原tokens修改)生成superMatcher并调用344     compile( selector, match )( seed, context, results );345     return results;346 }347 348 // 对外入口349 window.MSizzle = Sizzle;350 351 })(window)352 // 测试353 console.log(MSizzle("ul.topnav > li"))354 console.log(MSizzle("ul.topnav   li"))355 console.log(MSizzle("ul.topnav + div"))356 console.log(MSizzle("ul.topnav ~ div"))

 1.

Sizzle一步步实现所有功能(层级选择)