jquery--选择器sizzle源码分析
1 (function( window, undefined ) {2 3 vari,4 cachedruns,//正在匹配第几个元素 5 Expr, //Sizzle.selectors的快捷方式 6 getText,//获取文本函数 7 isXML, //是否为xml 8 compile,//编译函数 9 outermostContext,10 recompare,11 sortInput,12 13 //Local document vars 14 setDocument,15 document,//这里只是document是个普通的变量,需要setDocument函数赋值处理过的真正的document 16 docElem,17 documentIsHTML,18 rbuggyQSA,//支持的querySelectorAll选择器正则(rbuggyQSA = new RegExp( rbuggyQSA.join("|") );) 19 rbuggyMatches,//支持matchesSelector正则(rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );) 20 matches,//matchesSelector函数 21 contains,22 23 //Instance-specific data 24 expando = "sizzle" + -(newDate()),25 preferredDoc = window.document,//全局的document 26 support ={},27 dirruns = 0,28 done = 0,//第几个关系选择器匹配函数 29 classCache =createCache(),30 tokenCache =createCache(),31 compilerCache =createCache(),32 hasDuplicate = false,33 sortOrder = function() { return 0; },34 35 //General-purpose constants 36 strundefined = typeofundefined,37 MAX_NEGATIVE = 1 << 31,38 39 //Array methods 40 arr =[],41 pop =arr.pop,42 push_native =arr.push,43 push =arr.push,44 slice =arr.slice,45 //Use a stripped-down indexOf if we can't use a native one 46 //如果数组原生不支持indexOf,我们自己定义。获取元素在数组中的位置 47 indexOf = arr.indexOf || function( elem ) {48 var i = 0,49 len = this.length;50 for ( ; i < len; i++) {51 if ( this[i] ===elem ) {52 returni;53 }54 }55 return -1;56 },57 58 59 //正则表达式 60 61 //空白字符正则字符串 62 whitespace = "[\\x20\\t\\r\\n\\f]",63 //字符编码正则字符串 64 characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",65 66 identifier = characterEncoding.replace( "w", "w#"),67 68 //可用的属性操作符 69 operators = "([*^$|!~]?=)",70 //属性选择器正则 71 ///^\[whitespace*(characterEncoding)whitespace*(?:([*^$|!~]?=)whitespace*(?:(['"])((?:\\.|[^\\])*?)\3|((?:\\.|[\w#-]|[^\x00-\xa0])+)|)|)whitespace*\]/ 72 attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + 73 "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",74 75 //伪类正则字符串 76 pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)",77 78 //去掉两端空白和字符串中的反斜杠(如果连续两个去掉一个)正则 79 rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g"),80 //并联选择器的正则 81 rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*"),82 //关系选择器正则 83 rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*"),84 //伪类正则字符串 85 rpseudo = newRegExp( pseudos ),86 ridentifier = new RegExp( "^" + identifier + "$"),87 88 matchExpr ={89 "ID": new RegExp( "^#(" + characterEncoding + ")"),90 "CLASS": new RegExp( "^\\.(" + characterEncoding + ")"),91 "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]"),92 "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")"),93 "ATTR": new RegExp( "^" +attributes ),94 "PSEUDO": new RegExp( "^" +pseudos ),95 "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + 96 "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + 97 "*(\\d+)|))" + whitespace + "*\\)|)", "i"),98 //开始为>+~或位置伪类,如果选择器中有位置伪类解析从左往右 99 "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + 100 whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i")101 },102 //弟兄正则 103 rsibling = /[\x20\t\r\n\f]*[+~]/,104 //原生函数正则 105 rnative = /^[^{]+\{\s*\[native code/,106 107 //仅仅单个id或tag、class选择器正则(用来快速解析并获取元素) 108 rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,109 //jQuery自定义的伪类,它们不是的CSS规范的一部分,使用它们查询不能充分利用原生DOM提供的querySelectorAll() 方法来提高性能。为了在现代浏览器上获得更佳的性能,请使用 .filter(":input")代替。 110 rinputs = /^(?:input|select|textarea|button)$/i,111 //同上 112 rheader = /^h\d$/i,113 114 rescape = /'|\\/g,115 //属性没带引号正则 116 rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,117 118 //css转义,配合funescape使用 如:string.replace(runescape, funescape); 119 runescape = /\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,120 funescape = function( _, escaped ) {121 var high = "0x" + escaped - 0x10000;122 //NaN means non-codepoint 123 return high !== high ? 124 escaped :125 //BMP codepoint 126 high < 0 ? 127 String.fromCharCode( high + 0x10000) :128 //Supplemental Plane codepoint (surrogate pair) 129 String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00);130 };131 132 //把NodeList转换为数组,因为数组比集合效率高 133 try{134 push.apply(135 (arr =slice.call( preferredDoc.childNodes )),136 preferredDoc.childNodes137 );138 //Support: Android<4.0 139 //Detect silently failing push.apply 140 arr[ preferredDoc.childNodes.length ].nodeType;141 } catch( e ) {142 push = { apply: arr.length ? 143 144 //Leverage slice if possible 145 function( target, els ) {146 push_native.apply( target, slice.call(els) );147 } :148 149 //Support: IE<9 150 //Otherwise append directly 151 function( target, els ) {152 var j =target.length,153 i = 0;154 //Can't trust NodeList.length 155 while ( (target[j++] = els[i++]) ) {}156 target.length = j - 1;157 }158 };159 }160 161 //测试是否是原生函数 162 functionisNative( fn ) {163 return rnative.test( fn + "");164 }165 166 //创建缓存函数(缓存大小默认为50,可以自己设置) 167 //这里利用了闭包(私有变量、变量一直保存在内存中) 168 functioncreateCache() {169 varcache,170 keys =[];171 172 return (cache = function( key, value ) {173 //Use (key + " ") to avoid collision with native prototype properties (see Issue #157) 174 if ( keys.push( key += " " ) >Expr.cacheLength ) {175 //Only keep the most recent entries 176 deletecache[ keys.shift() ];177 }178 return (cache[ key ] =value);179 });180 }181 182 //标记函数供sizzle特殊用途 183 functionmarkFunction( fn ) {184 fn[ expando ] = true;185 returnfn;186 }187 188 //用于做各种特征检测,比如是否支持某个API,API支持是否完美 189 190 functionassert( fn ) {191 var div = document.createElement("div");192 193 try{194 return !!fn( div );195 } catch(e) {196 return false;197 } finally{198 //release memory in IE 199 div = null;200 }201 }202 203 //sizzle主函数 204 //selector css选择器, context上下文,results结果集,seed筛选集 205 206 functionSizzle( selector, context, results, seed ) {207 varmatch, elem, m, nodeType,208 //QSA vars 209 i, groups, old, nid, newContext, newSelector;210 211 if ( ( context ? context.ownerDocument || context : preferredDoc ) !==document ) {212 setDocument( context );213 }214 215 context = context ||document;216 results = results ||[];217 218 if ( !selector || typeof selector !== "string") {219 returnresults;220 }221 222 if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9) {223 return[];224 }225 226 if ( documentIsHTML && !seed ) {227 228 //Shortcuts 229 //如果仅仅是id或class或tag用原生函数 230 if ( (match =rquickExpr.exec( selector )) ) {231 //Speed-up: Sizzle("#ID") 232 if ( (m = match[1]) ) {233 if ( nodeType === 9) {234 elem =context.getElementById( m );235 //Check parentNode to catch when Blackberry 4.6 returns 236 //nodes that are no longer in the document #6963 237 if ( elem &&elem.parentNode ) {238 //Handle the case where IE, Opera, and Webkit return items 239 //by name instead of ID 240 if ( elem.id ===m ) {241 results.push( elem );242 returnresults;243 }244 } else{245 returnresults;246 }247 } else{248 //Context is not a document 249 if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && 250 contains( context, elem ) && elem.id ===m ) {251 results.push( elem );252 returnresults;253 }254 }255 256 //Speed-up: Sizzle("TAG") 257 } else if ( match[2] ) {258 push.apply( results, context.getElementsByTagName( selector ) );259 returnresults;260 261 //Speed-up: Sizzle(".CLASS") 262 } else if ( (m = match[3]) && support.getElementsByClassName &&context.getElementsByClassName ) {263 push.apply( results, context.getElementsByClassName( m ) );264 returnresults;265 }266 }267 268 //QSA path 269 //使用querySelectorAll 270 if ( !support.qsa && !rbuggyQSA.test(selector) ) {271 old = true;272 nid =expando;273 newContext =context;274 newSelector = nodeType === 9 &&selector;275 276 //qSA works strangely on Element-rooted queries 277 //We can work around this by specifying an extra ID on the root 278 //and working up from there (Thanks to Andrew Dupont for the technique) 279 //IE 8 doesn't work on object elements 280 if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object") {281 groups =tokenize( selector );282 283 if ( (old = context.getAttribute("id")) ) {284 nid = old.replace( rescape, "\\$&");285 } else{286 context.setAttribute( "id", nid );287 }288 nid = "[id='" + nid + "'] ";289 290 i =groups.length;291 while ( i--) {292 groups[i] = nid +toSelector( groups[i] );293 }294 newContext = rsibling.test( selector ) && context.parentNode ||context;295 newSelector = groups.join(",");296 }297 298 if( newSelector ) {299 try{300 push.apply( results,301 newContext.querySelectorAll( newSelector )302 );303 returnresults;304 } catch(qsaError) {305 } finally{306 if ( !old ) {307 context.removeAttribute("id");308 }309 }310 }311 }312 }313 314 //All others 315 return select( selector.replace( rtrim, "$1"), context, results, seed );316 }317 318 //检测是否为xml 319 isXML = Sizzle.isXML = function( elem ) {320 //documentElement is verified for cases where it doesn't yet exist 321 //(such as loading iframes in IE - #4833) 322 var documentElement = elem && (elem.ownerDocument ||elem).documentElement;323 return documentElement ? documentElement.nodeName !== "HTML" : false;324 };325 326 327 //根据当前的document设置document相关的变量(只设置一次) 328 setDocument = Sizzle.setDocument = function( node ) {329 var doc = node ? node.ownerDocument ||node : preferredDoc;330 331 //如果document已经设置了返回(这里document是个变量初始值是undefined) 332 //不是文档对象返回 333 //文档对象没有返回根节点时返回 334 if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {335 returndocument;336 }337 338 //给document变量设置真正的文档 339 document =doc;340 docElem =doc.documentElement;341 342 documentIsHTML = !isXML( doc );343 344 //检查getElementsByTagName是否会返回注释节点,IE6-8会混杂注释节点 345 support.getElementsByTagName = assert(function( div ) {346 div.appendChild( doc.createComment("") );347 return !div.getElementsByTagName("*").length;348 });349 350 //Check if attributes should be retrieved by attribute nodes 351 support.attributes = assert(function( div ) {352 div.innerHTML = "<select></select>";353 var type = typeof div.lastChild.getAttribute("multiple");354 //IE8 returns a string for some attributes even when not present 355 return type !== "boolean" && type !== "string";356 });357 358 //检查getElementsByClassName是否100%支持 359 support.getElementsByClassName = assert(function( div ) {360 //Oprea9.6不可以发现第二个className 361 div.innerHTML = "<div class='hidden e'></div><div class='hidden'></div>";362 if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) {363 return false;364 }365 366 //safari3.2缓存了class属性,所以不能获取改变过的classname 367 div.lastChild.className = "e";368 return div.getElementsByClassName("e").length === 2;369 });370 371 //检查getElementByName 372 support.getByName = assert(function( div ) {373 //Inject content 374 div.id = expando + 0;375 //Support: Windows 8 Native Apps 376 //Assigning innerHTML with "name" attributes throws uncatchable exceptions 377 //http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx 378 //getElementsByName是一个问题多的API 379 //1 IE6-7下getElementsByName与getElementById都不区分元素的name与ID 380 //2 IE的getElementsByName只对表单元素有效,无视拥有相同name值的span div元素 381 //3 IE6-7下即使通过document.createElement创建一个表单元素,动态设置name与插入 382 //DOM树,getElementsByName无法找到此元素,innerHTML也不行。一定需要以 383 //document.createElement("<input name="aaa"/>")方式生成元素才行。 384 //同样的情况也发生在iframe上,IE6-7的iframe的name也需要这样同时生成。 385 //4 name本来是一个property,但标准浏览器好像已经默认setAttribute("name","xxx") 386 //也能被getElementsByName获取到。 387 //5 IE6-8通过innerHTML生成包含name属性的元素时,可能发生无法捕获的错误 388 div.appendChild( document.createElement("a") ).setAttribute( "name", expando );389 div.appendChild( document.createElement("i") ).setAttribute( "name", expando );390 docElem.appendChild( div );391 392 //Test 393 var pass = doc.getElementsByName && 394 //buggy browsers will return fewer than the correct 2 395 doc.getElementsByName( expando ).length === 2 + 396 //buggy browsers will return more than the correct 0 397 doc.getElementsByName( expando + 0).length;398 399 //Cleanup 400 docElem.removeChild( div );401 402 returnpass;403 });404 405 //Support: Webkit<537.32 406 //Detached nodes confoundingly follow *each other* 407 support.sortDetached = assert(function( div1 ) {408 return div1.compareDocumentPosition && 409 //Should return 1, but Webkit returns 4 (following) 410 (div1.compareDocumentPosition( document.createElement("div") ) & 1);411 });412 413 //ie6/7 href和type属性获取 414 Expr.attrHandle = assert(function( div ) {415 div.innerHTML = "<a href='#'></a>";416 return div.firstChild && typeof div.firstChild.getAttribute !== strundefined && 417 div.firstChild.getAttribute("href") === "#";418 }) ? 419 {} :420 {421 "href": function( elem ) {422 return elem.getAttribute( "href", 2);423 },424 "type": function( elem ) {425 return elem.getAttribute("type");426 }427 };428 429 //查找id节点和节点id是否匹配过滤函数 430 if( support.getByName ) {431 Expr.find["ID"] = function( id, context ) {432 if ( typeof context.getElementById !== strundefined &&documentIsHTML ) {433 var m =context.getElementById( id );434 //Check parentNode to catch when Blackberry 4.6 returns 435 //nodes that are no longer in the document #6963 436 //Blackberry 4.6 缓存过度,即便这元素被移出DOM树也能找到 437 return m && m.parentNode ?[m] : [];438 }439 };440 Expr.filter["ID"] = function( id ) {441 var attrId =id.replace( runescape, funescape );442 return function( elem ) {443 return elem.getAttribute("id") ===attrId;444 };445 };446 } else{447 Expr.find["ID"] = function( id, context ) {448 if ( typeof context.getElementById !== strundefined &&documentIsHTML ) {449 var m =context.getElementById( id );450 451 return m ? 452 m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ? 453 [m] :454 undefined :455 [];456 }457 };458 Expr.filter["ID"] = function( id ) {459 var attrId =id.replace( runescape, funescape );460 return function( elem ) {461 var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");462 return node && node.value ===attrId;463 };464 };465 }466 467 //根据标签查找节点 468 Expr.find["TAG"] = support.getElementsByTagName ? 469 function( tag, context ) {470 if ( typeof context.getElementsByTagName !==strundefined ) {471 returncontext.getElementsByTagName( tag );472 }473 } :474 function( tag, context ) {475 varelem,476 tmp =[],477 i = 0,478 results =context.getElementsByTagName( tag );479 480 //Filter out possible comments 481 if ( tag === "*") {482 while ( (elem = results[i++]) ) {483 if ( elem.nodeType === 1) {484 tmp.push( elem );485 }486 }487 488 returntmp;489 }490 returnresults;491 };492 493 //如果支持根据name查找节点 494 Expr.find["NAME"] = support.getByName && function( tag, context ) {495 if ( typeof context.getElementsByName !==strundefined ) {496 returncontext.getElementsByName( name );497 }498 };499 500 //如果支持根据class查找节点 501 Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {502 if ( typeof context.getElementsByClassName !== strundefined &&documentIsHTML ) {503 returncontext.getElementsByClassName( className );504 }505 };506 507 //QSA and matchesSelector support 508 509 rbuggyMatches = [];//支持的matchesSelector放到数组中 510 511 rbuggyQSA = [ ":focus" ];//支持的querySelectorAll放到数组中 512 513 if ( (support.qsa =isNative(doc.querySelectorAll)) ) {514 //Build QSA regex 515 //Regex strategy adopted from Diego Perini 516 assert(function( div ) {517 //Select is set to empty string on purpose 518 //This is to test IE's treatment of not explicitly 519 //setting a boolean content attribute, 520 //since its presence should be enough 521 //http://bugs.jquery.com/ticket/12359 522 div.innerHTML = "<select><option selected=''></option></select>";523 524 //IE8 - Some boolean attributes are not treated correctly 525 if ( !div.querySelectorAll("[selected]").length ) {526 rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)");527 }528 529 //Webkit/Opera - :checked should return selected option elements 530 //http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked 531 //IE8 throws error here and will not see later tests 532 if ( !div.querySelectorAll(":checked").length ) {533 rbuggyQSA.push(":checked");534 }535 });536 537 assert(function( div ) {538 539 //Opera 10-12/IE8 - ^= $= *= and empty values 540 //Should not select anything 541 div.innerHTML = "<input type='hidden' i=''/>";542 if ( div.querySelectorAll("[i^='']").length ) {543 rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')");544 }545 546 //FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) 547 //IE8 throws error here and will not see later tests 548 if ( !div.querySelectorAll(":enabled").length ) {549 rbuggyQSA.push( ":enabled", ":disabled");550 }551 552 //Opera 10-11 does not throw on post-comma invalid pseudos 553 div.querySelectorAll("*,:x");554 rbuggyQSA.push(",.*:");555 });556 }557 558 if ( (support.matchesSelector = isNative( (matches = docElem.matchesSelector || 559 docElem.mozMatchesSelector || 560 docElem.webkitMatchesSelector || 561 docElem.oMatchesSelector || 562 docElem.msMatchesSelector) )) ) {563 564 assert(function( div ) {565 //ie9能匹配移出DOM树的节点 566 support.disconnectedMatch = matches.call( div, "div");567 568 //Gecko对于非法选择符不会抛错,而是返回false 569 matches.call( div, "[s!='']:x");570 rbuggyMatches.push( "!=", pseudos );571 });572 }573 574 rbuggyQSA = new RegExp( rbuggyQSA.join("|") );575 rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );576 577 //Element contains another 578 //Purposefully does not implement inclusive descendent 579 //As in, an element does not contain itself 580 contains = isNative(docElem.contains) || docElem.compareDocumentPosition ? 581 function( a, b ) {582 var adown = a.nodeType === 9 ?a.documentElement : a,583 bup = b &&b.parentNode;584 return a === bup || !!( bup && bup.nodeType === 1 &&(585 adown.contains ? 586 adown.contains( bup ) :587 a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 588 ));589 } :590 function( a, b ) {591 if( b ) {592 while ( (b =b.parentNode) ) {593 if ( b ===a ) {594 return true;595 }596 }597 }598 return false;599 };600 601 //文档顺序排序 602 sortOrder = docElem.compareDocumentPosition ? 603 function( a, b ) {604 605 //重复删除标记 606 if ( a ===b ) {607 hasDuplicate = true;608 return 0;609 }610 611 var compare = b.compareDocumentPosition && a.compareDocumentPosition &&a.compareDocumentPosition( b );612 613 if( compare ) {614 //Disconnected nodes 615 if ( compare & 1 || 616 (recompare && b.compareDocumentPosition( a ) ===compare) ) {617 618 //Choose the first element that is related to our preferred document 619 if ( a === doc ||contains(preferredDoc, a) ) {620 return -1;621 }622 if ( b === doc ||contains(preferredDoc, b) ) {623 return 1;624 }625 626 //Maintain original order 627 return sortInput ? 628 ( indexOf.call( sortInput, a ) -indexOf.call( sortInput, b ) ) :629 0;630 }631 632 return compare & 4 ? -1 : 1;633 }634 635 //不能直接比较,sort上不存在这个方法 636 return a.compareDocumentPosition ? -1 : 1;637 } :638 function( a, b ) {639 varcur,640 i = 0,641 aup =a.parentNode,642 bup =b.parentNode,643 ap =[ a ],644 bp =[ b ];645 646 //Exit early if the nodes are identical 647 if ( a ===b ) {648 hasDuplicate = true;649 return 0;650 651 //Parentless nodes are either documents or disconnected 652 } else if ( !aup || !bup ) {653 return a === doc ? -1:654 b === doc ? 1:655 aup ? -1:656 bup ? 1:657 0;658 659 //If the nodes are siblings, we can do a quick check 660 } else if ( aup ===bup ) {661 returnsiblingCheck( a, b );662 }663 664 //Otherwise we need full lists of their ancestors for comparison 665 cur =a;666 while ( (cur =cur.parentNode) ) {667 ap.unshift( cur );668 }669 cur =b;670 while ( (cur =cur.parentNode) ) {671 bp.unshift( cur );672 }673 674 //Walk down the tree looking for a discrepancy 675 while ( ap[i] ===bp[i] ) {676 i++;677 }678 679 return i ? 680 //Do a sibling check if the nodes have a common ancestor 681 siblingCheck( ap[i], bp[i] ) :682 683 //Otherwise nodes in our document sort first 684 ap[i] === preferredDoc ? -1:685 bp[i] === preferredDoc ? 1:686 0;687 };688 689 returndocument;690 };691 //筛选匹配的elements 692 Sizzle.matches = function( expr, elements ) {693 return Sizzle( expr, null, null, elements );694 };695 696 Sizzle.matchesSelector = function( elem, expr ) {697 //Set document vars if needed 698 if ( ( elem.ownerDocument || elem ) !==document ) {699 setDocument( elem );700 }701 702 //确保属性选择器有引号(querySelectorAll需要有引号) 703 expr = expr.replace( rattributeQuotes, "='$1']");704 705 //rbuggyQSA中肯定存在:focus,所以不需要检查是否存在 706 if ( support.matchesSelector && documentIsHTML && (!rbuggyMatches || !rbuggyMatches.test(expr)) && !rbuggyQSA.test(expr) ) {707 try{708 var ret =matches.call( elem, expr );709 710 //ie9和文档分离的node返回false 711 //同样在ie9中文档片段也是分离的节点(createElement('div')这样创建的节点有document属性.document.nodeType为11) 712 if ( ret || support.disconnectedMatch || elem.document && elem.document.nodeType !== 11) {713 returnret;714 }715 } catch(e) {}716 }717 718 return Sizzle( expr, document, null, [elem] ).length > 0;719 };720 721 Sizzle.contains = function( context, elem ) {722 //Set document vars if needed 723 if ( ( context.ownerDocument || context ) !==document ) {724 setDocument( context );725 }726 returncontains( context, elem );727 };728 729 Sizzle.attr = function( elem, name ) {730 varval;731 732 //Set document vars if needed 733 if ( ( elem.ownerDocument || elem ) !==document ) {734 setDocument( elem );735 }736 737 if( documentIsHTML ) {738 name =name.toLowerCase();739 }740 if ( (val =Expr.attrHandle[ name ]) ) {741 returnval( elem );742 }743 if ( !documentIsHTML ||support.attributes ) {744 returnelem.getAttribute( name );745 }746 return ( (val = elem.getAttributeNode( name )) || elem.getAttribute( name ) ) && elem[ name ] === true ? 747 name :748 val && val.specified ? val.value : null;749 };750 751 Sizzle.error = function( msg ) {752 throw new Error( "Syntax error, unrecognized expression: " +msg );753 };754 755 //排序并去重 756 Sizzle.uniqueSort = function( results ) {757 varelem,758 duplicates =[],759 j = 0,760 i = 0;761 762 //Unless we *know* we can detect duplicates, assume their presence 763 //除非我们知道我们可以检测到重复的,假设他们的存在 764 hasDuplicate = !support.detectDuplicates;765 //Compensate for sort limitations 766 recompare = !support.sortDetached;767 sortInput = !support.sortStable && results.slice( 0);768 results.sort( sortOrder );769 770 if( hasDuplicate ) {771 while ( (elem = results[i++]) ) {772 if ( elem ===results[ i ] ) {773 j =duplicates.push( i );774 }775 }776 while ( j--) {777 results.splice( duplicates[ j ], 1);778 }779 }780 781 returnresults;782 };783 784 //兄弟节点排序 785 786 functionsiblingCheck( a, b ) {787 var cur = b &&a,788 diff = cur && ( ~b.sourceIndex || MAX_NEGATIVE ) - ( ~a.sourceIndex ||MAX_NEGATIVE );789 790 //Use IE sourceIndex if available on both nodes 791 if( diff ) {792 returndiff;793 }794 795 //Check if b follows a 796 if( cur ) {797 while ( (cur =cur.nextSibling) ) {798 if ( cur ===b ) {799 return -1;800 }801 }802 }803 804 return a ? 1 : -1;805 }806 807 //创建一个伪类的过滤函数,此方法是根据表单元素的type值生成 808 //比如:radio, :text, :checkbox, :file, :image等自定义伪类 809 functioncreateInputPseudo( type ) {810 return function( elem ) {811 var name =elem.nodeName.toLowerCase();812 return name === "input" && elem.type ===type;813 };814 }815 816 //创建一个伪类的过滤函数,此方法是根据表单元素的type值或标签类型生成 817 //如果:button, :submit自定义伪类 818 functioncreateButtonPseudo( type ) {819 return function( elem ) {820 var name =elem.nodeName.toLowerCase();821 return (name === "input" || name === "button") && elem.type ===type;822 };823 }824 825 //用于创建位置伪类的过滤函数,它们是模拟从左向右的顺序进行选择, 826 //匹配到它时的结果集的位置来挑选元素的 827 //比如:odd,:even, :eq, :gt, :lt, :first, :last 828 functioncreatePositionalPseudo( fn ) {829 return markFunction(function( argument ) {830 argument = +argument;831 return markFunction(function( seed, matches ) {832 varj,833 matchIndexes =fn( [], seed.length, argument ),834 i =matchIndexes.length;835 836 //Match elements found at the specified indexes 837 while ( i--) {838 if ( seed[ (j =matchIndexes[i]) ] ) {839 seed[j] = !(matches[j] = seed[j]);//这里的j是数组索引值,matches数组中可能会出现undefinde值 如:var arr = []; arr[10] = 10;这里前9项会是undefined 840 }841 }842 });843 });844 }845 846 //工具函数用于获取给定节点的text 847 getText = Sizzle.getText = function( elem ) {848 varnode,849 ret = "",850 i = 0,851 nodeType =elem.nodeType;852 853 if ( !nodeType ) {854 //If no nodeType, this is expected to be an array 855 for ( ; (node = elem[i]); i++) {856 //Do not traverse comment nodes 857 ret +=getText( node );858 }859 } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11) {860 //Use textContent for elements 861 //innerText usage removed for consistency of new lines (see #11153) 862 if ( typeof elem.textContent === "string") {863 returnelem.textContent;864 } else{865 //Traverse its children 866 for ( elem = elem.firstChild; elem; elem =elem.nextSibling ) {867 ret +=getText( elem );868 }869 }870 } else if ( nodeType === 3 || nodeType === 4) {871 returnelem.nodeValue;872 }873 //Do not include comment or processing instruction nodes 874 875 returnret;876 };877 878 Expr = Sizzle.selectors ={879 880 //Can be adjusted by the user 881 cacheLength: 50,882 883 createPseudo: markFunction,884 885 match: matchExpr,886 887 find: {},//查找元素 888 889 relative: {890 ">": { dir: "parentNode", first: true},891 " ": { dir: "parentNode"},892 "+": { dir: "previousSibling", first: true},893 "~": { dir: "previousSibling"}894 },895 896 preFilter: {//正则出来的数组不能直接用需要过滤下如:attr正则出来的数组引号还有~= 897 "ATTR": function( match ) {898 match[1] = match[1].replace( runescape, funescape );899 900 //不管有没有引号都把value值移到match[3] 901 //有引号["[name="username"]", "name", "=", """, "username", undefined] 902 //无引号["[name=username]", "name", "=", undefined, undefined, "username"] 903 match[3] = ( match[4] || match[5] || "").replace( runescape, funescape );904 905 if ( match[2] === "~=" ) {//匹配属性值必须有空格分隔(这个选择器测试属性值中的每个单词字符串,其中“word”是一个由空白分隔的字符串定义的) 906 match[3] = " " + match[3] + " ";907 }908 909 return match.slice( 0, 4);910 },911 912 "CHILD": function( match ) {913 /*matches from matchExpr["CHILD"]914 1 type (only|nth|...)915 2 what (child|of-type)916 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)917 4 xn-component of xn+y argument ([+-]?\d*n|)918 5 sign of xn-component919 6 x of xn-component920 7 sign of y-component921 8 y of y-component922 */ 923 //[":nth-child(2n + 1)", "nth", "child", "2n + 1", "2n", "", "2", "+", "1"] 924 //[":nth-last-child(1)", "nth-last", "child", "1", "", undefined, undefined, "", "1"] 925 //[":nth-child(even)", "nth", "child", "even", undefined, undefined, undefined, undefined, undefined] 926 match[1] = match[1].toLowerCase();927 //当选择器中有nth时 928 if ( match[1].slice( 0, 3 ) === "nth") {929 //nth-* requires argument 930 //当选择器中有nth时小括号内没有值说明不是合法的CHILD选择器 931 if ( !match[3] ) {932 Sizzle.error( match[0] );933 }934 935 //numeric x and y parameters for Expr.filter.CHILD 936 //remember that false/true cast respectively to 0/1 937 //处理(xn + y) 938 //true和false分别转换为1和0, true和false参与运算时会自动转换为1和0 939 match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd") );940 match[5] = +( ( match[7] + match[8] ) || match[3] === "odd");941 942 //other types prohibit arguments 943 //如果不是nth类型但是match[3]有值说明不是合法的CHILD选择器 944 } else if ( match[3] ) {945 Sizzle.error( match[0] );946 }947 948 returnmatch;949 },950 951 "PSEUDO": function( match ) {952 varexcess,953 unquoted = !match[5] && match[2];954 955 //如果是孩子伪类不处理 956 if ( matchExpr["CHILD"].test( match[0] ) ) {957 return null;958 }959 //[":has(input[name=username])", "has", "input[name=username]", undefined, undefined, "input[name=username]", "name", "=", undefined, undefined, "username"] 960 //Accept quoted arguments as-is 961 //如果小括号内有引号 如:[":eq("2")", "eq", ""2"", """, "2", undefined, undefined, undefined, undefined, undefined, undefined] 962 if ( match[4] ) {963 match[2] = match[4];964 965 //Strip excess characters from unquoted arguments 966 //不知道什么伪类会走这里 967 } else if ( unquoted && rpseudo.test( unquoted ) && 968 //Get excess from tokenize (recursively) 969 (excess = tokenize( unquoted, true )) && 970 //advance to the next closing parenthesis 971 (excess = unquoted.indexOf( ")", unquoted.length - excess ) -unquoted.length) ) {972 973 //excess is a negative index 974 match[0] = match[0].slice( 0, excess );975 match[2] = unquoted.slice( 0, excess );976 }977 978 //返回捕获中仅仅需要的过滤方法(type和argument) 979 return match.slice( 0, 3);980 }981 },982 983 filter: {//过滤函数(判断各节点是否符合条件) 984 985 "TAG": function( nodeName ) {986 if ( nodeName === "*") {987 return function() { return true; };988 }989 990 nodeName =nodeName.replace( runescape, funescape ).toLowerCase();991 return function( elem ) {992 return elem.nodeName && elem.nodeName.toLowerCase() ===nodeName;993 };994 },995 996 "CLASS": function( className ) {997 var pattern = classCache[ className + " "];998 999 return pattern || 1000 (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && 1001 classCache( className, function( elem ) {1002 return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "");1003 });1004 },1005 1006 "ATTR": function( name, operator, check ) {1007 return function( elem ) {1008 var result =Sizzle.attr( elem, name );1009 1010 if ( result == null) {1011 return operator === "!=";1012 }1013 if ( !operator ) {1014 return true;1015 }1016 1017 result += "";1018 1019 return operator === "=" ? result ===check :1020 operator === "!=" ? result !==check :1021 operator === "^=" ? check && result.indexOf( check ) === 0:1022 operator === "*=" ? check && result.indexOf( check ) > -1:1023 operator === "$=" ? check && result.slice( -check.length ) ===check :1024 operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1:1025 operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-":1026 false;1027 };1028 },1029 1030 //:nth-child(2) 1031 //["nth", "child", "2", 0, 2, undefined, "", "2"] 1032 "CHILD": function( type, what, argument, first, last ) {1033 var simple = type.slice( 0, 3 ) !== "nth", //是否有nth 1034 forward = type.slice( -4 ) !== "last",//是否正向查找 1035 ofType = what === "of-type"; //是否有of-type 1036 1037 return first === 1 && last === 0 ? 1038 1039 //Shortcut for :nth-*(n) 1040 function( elem ) {1041 return !!elem.parentNode;1042 } :1043 1044 function( elem, context, xml ) {1045 varcache, outerCache, node, diff, nodeIndex, start,1046 dir = simple !== forward ? "nextSibling" : "previousSibling",1047 parent =elem.parentNode,1048 name = ofType &&elem.nodeName.toLowerCase(),1049 useCache = !xml && !ofType;1050 1051 if( parent ) {1052 1053 //:(first|last|only)-(child|of-type) 1054 if( simple ) {1055 while( dir ) {1056 node =elem;1057 while ( (node =node[ dir ]) ) {1058 //如果不是of-type, first如果前面还有元素返回false, last如果后面还有元素返回false 1059 //如果是of-type, name值全等返回false 1060 if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1) {1061 return false;1062 }1063 }1064 //Reverse direction for :only-* (if we haven't yet done so) 1065 //如果type为only时, 1066 start = dir = type === "only" && !start && "nextSibling";1067 }1068 return true;1069 }1070 1071 start = [ forward ?parent.firstChild : parent.lastChild ];1072 1073 //non-xml :nth-child(...) stores cache data on `parent` 1074 if ( forward &&useCache ) {1075 //Seek `elem` from a previously-cached index 1076 outerCache = parent[ expando ] || (parent[ expando ] ={});1077 cache = outerCache[ type ] ||[];1078 nodeIndex = cache[0] === dirruns && cache[1];1079 diff = cache[0] === dirruns && cache[2];1080 node = nodeIndex &&parent.childNodes[ nodeIndex ];1081 1082 while ( (node = ++nodeIndex && node && node[ dir ] || 1083 1084 //Fallback to seeking `elem` from the start 1085 //第一次执行while循环时走这里(确切的说应该是第一个遍历childNodes节点时)如果start内没有节点则循环一次找到节点,这也是diff = nodeIndex = 0的原因 1086 (diff = nodeIndex = 0) ||start.pop()) ) {1087 1088 //When found, cache indexes on `parent` and break 1089 //当获取到的element,把这个element的索引值缓存在父节点[dirruns, 节点索引值, 真正的节点(不是文本或注释节点)] 1090 if ( node.nodeType === 1 && ++diff && node ===elem ) {1091 outerCache[ type ] =[ dirruns, nodeIndex, diff ];1092 break;1093 }1094 }1095 1096 //Use previously-cached element index if available 1097 } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] ===dirruns ) {1098 diff = cache[1];1099 1100 //如果是xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) 1101 } else{1102 //Use the same loop as above to seek `elem` from the start 1103 while ( (node = ++nodeIndex && node && node[ dir ] || 1104 (diff = nodeIndex = 0) ||start.pop()) ) {1105 1106 if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {1107 //Cache the index of each encountered element 1108 if( useCache ) {1109 (node[ expando ] || (node[ expando ] = {}))[ type ] =[ dirruns, diff ];1110 }1111 1112 if ( node ===elem ) {1113 break;1114 }1115 }1116 }1117 }1118 1119 //Incorporate the offset, then check against cycle size 1120 diff -=last;1121 return diff === first || ( diff % first === 0 && diff / first >= 0);1122 }1123 };1124 },1125 1126 "PSEUDO": function( pseudo, argument ) {1127 1128 //伪类names值不区分大小写(不对) 1129 //优先大小写敏感的定制伪类 1130 //setFilters继承了pseudos 1131 varargs,1132 fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || 1133 Sizzle.error( "unsupported pseudo: " +pseudo );1134 //上面的fn才是真正的筛选函数(有sizzle自己定义的,用户也可以自定义) 1135 1136 1137 //用户可能用createPseuso(需要创建一个过滤函数做为参数)自定义一个伪类, 就像sizzle创建的一样 1138 if( fn[ expando ] ) {1139 returnfn( argument );1140 }1141 1142 //保持旧的签名支持 1143 if ( fn.length > 1) {1144 args = [ pseudo, pseudo, "", argument ];1145 return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? 1146 markFunction(function( seed, matches ) {1147 varidx,1148 matched =fn( seed, argument ),1149 i =matched.length;1150 while ( i--) {1151 idx =indexOf.call( seed, matched[i] );1152 seed[ idx ] = !( matches[ idx ] =matched[i] );1153 }1154 }) :1155 function( elem ) {1156 return fn( elem, 0, args );1157 };1158 }1159 1160 returnfn;1161 }1162 },1163 1164 pseudos: {1165 //Potentially complex pseudos 1166 "not": markFunction(function( selector ) {1167 //Trim the selector passed to compile 1168 //to avoid treating leading and trailing 1169 //spaces as combinators 1170 var input =[],1171 results =[],1172 matcher = compile( selector.replace( rtrim, "$1") );1173 1174 return matcher[ expando ] ? 1175 markFunction(function( seed, matches, context, xml ) {1176 varelem,1177 unmatched = matcher( seed, null, xml, [] ),1178 i =seed.length;1179 1180 //Match elements unmatched by `matcher` 1181 while ( i--) {1182 if ( (elem =unmatched[i]) ) {1183 seed[i] = !(matches[i] =elem);1184 }1185 }1186 }) :1187 function( elem, context, xml ) {1188 input[0] =elem;1189 matcher( input, null, xml, results );1190 return !results.pop();1191 };1192 }),1193 1194 "has": markFunction(function( selector ) {1195 return function( elem ) {1196 return Sizzle( selector, elem ).length > 0;1197 };1198 }),1199 1200 "contains": markFunction(function( text ) {1201 return function( elem ) {1202 return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;1203 };1204 }),1205 1206 //"Whether an element is represented by a :lang() selector 1207 //is based solely on the element's language value 1208 //being equal to the identifier C, 1209 //or beginning with the identifier C immediately followed by "-". 1210 //The matching of C against the element's language value is performed case-insensitively. 1211 //The identifier C does not have to be a valid language name." 1212 //http://www.w3.org/TR/selectors/#lang-pseudo 1213 "lang": markFunction( function( lang ) {1214 //lang value must be a valid identifier 1215 if ( !ridentifier.test(lang || "") ) {1216 Sizzle.error( "unsupported lang: " +lang );1217 }1218 lang =lang.replace( runescape, funescape ).toLowerCase();1219 return function( elem ) {1220 varelemLang;1221 do{1222 if ( (elemLang = documentIsHTML ? 1223 elem.lang :1224 elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {1225 1226 elemLang =elemLang.toLowerCase();1227 return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;1228 }1229 } while ( (elem = elem.parentNode) && elem.nodeType === 1);1230 return false;1231 };1232 }),1233 1234 //Miscellaneous 1235 "target": function( elem ) {1236 var hash = window.location &&window.location.hash;1237 return hash && hash.slice( 1 ) ===elem.id;1238 },1239 1240 "root": function( elem ) {1241 return elem ===docElem;1242 },1243 1244 "focus": function( elem ) {1245 return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);1246 },1247 1248 //Boolean properties 1249 "enabled": function( elem ) {1250 return elem.disabled === false;1251 },1252 1253 "disabled": function( elem ) {1254 return elem.disabled === true;1255 },1256 1257 "checked": function( elem ) {1258 //In CSS3, :checked should return both checked and selected elements 1259 //http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked 1260 var nodeName =elem.nodeName.toLowerCase();1261 return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);1262 },1263 1264 "selected": function( elem ) {1265 //Accessing this property makes selected-by-default 1266 //options in Safari work properly 1267 if( elem.parentNode ) {1268 elem.parentNode.selectedIndex;1269 }1270 1271 return elem.selected === true;1272 },1273 1274 //Contents 1275 "empty": function( elem ) {1276 //http://www.w3.org/TR/selectors/#empty-pseudo 1277 //:empty is only affected by element nodes and content nodes(including text(3), cdata(4)), 1278 //not comment, processing instructions, or others 1279 //Thanks to Diego Perini for the nodeName shortcut 1280 //Greater than "@" means alpha characters (specifically not starting with "#" or "?") 1281 for ( elem = elem.firstChild; elem; elem =elem.nextSibling ) {1282 if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4) {1283 return false;1284 }1285 }1286 return true;1287 },1288 1289 "parent": function( elem ) {1290 return !Expr.pseudos["empty"]( elem );1291 },1292 1293 //Element/input types 1294 "header": function( elem ) {1295 returnrheader.test( elem.nodeName );1296 },1297 1298 "input": function( elem ) {1299 returnrinputs.test( elem.nodeName );1300 },1301 1302 "button": function( elem ) {1303 var name =elem.nodeName.toLowerCase();1304 return name === "input" && elem.type === "button" || name === "button";1305 },1306 1307 "text": function( elem ) {1308 varattr;1309 //IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) 1310 //use getAttribute instead to test this case 1311 return elem.nodeName.toLowerCase() === "input" && 1312 elem.type === "text" && 1313 ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() ===elem.type );1314 },1315 1316 //Position-in-collection 1317 "first": createPositionalPseudo(function() {1318 return [ 0];1319 }),1320 1321 "last": createPositionalPseudo(function( matchIndexes, length ) {1322 return [ length - 1];1323 }),1324 1325 "eq": createPositionalPseudo(function( matchIndexes, length, argument ) {1326 return [ argument < 0 ? argument +length : argument ];1327 }),1328 1329 "even": createPositionalPseudo(function( matchIndexes, length ) {1330 var i = 0;1331 for ( ; i < length; i += 2) {1332 matchIndexes.push( i );1333 }1334 returnmatchIndexes;1335 }),1336 1337 "odd": createPositionalPseudo(function( matchIndexes, length ) {1338 var i = 1;1339 for ( ; i < length; i += 2) {1340 matchIndexes.push( i );1341 }1342 returnmatchIndexes;1343 }),1344 1345 "lt": createPositionalPseudo(function( matchIndexes, length, argument ) {1346 var i = argument < 0 ? argument +length : argument;1347 for ( ; --i >= 0; ) {1348 matchIndexes.push( i );1349 }1350 returnmatchIndexes;1351 }),1352 1353 "gt": createPositionalPseudo(function( matchIndexes, length, argument ) {1354 var i = argument < 0 ? argument +length : argument;1355 for ( ; ++i <length; ) {1356 matchIndexes.push( i );1357 }1358 returnmatchIndexes;1359 })1360 }1361 };1362 1363 //添加input和button伪类 1364 for ( i in { radio: true, checkbox: true, file: true, password: true, image: true} ) {1365 Expr.pseudos[ i ] =createInputPseudo( i );1366 }1367 for ( i in { submit: true, reset: true} ) {1368 Expr.pseudos[ i ] =createButtonPseudo( i );1369 }1370 1371 //把selector解析成一个个独立的块 1372 functiontokenize( selector, parseOnly ) {1373 varmatched, match, tokens, type,1374 soFar, groups, preFilters,1375 cached = tokenCache[ selector + " "];1376 1377 if( cached ) {1378 return parseOnly ? 0 : cached.slice( 0);1379 }1380 1381 soFar =selector;1382 groups =[];1383 preFilters = Expr.preFilter;//正则出来的数组不能直接用需要过滤下如:attr正则出来的数组引号还有~= 1384 1385 while( soFar ) {1386 1387 //第一次运行或者并联选择器逗号 1388 if ( !matched || (match =rcomma.exec( soFar )) ) {1389 if( match ) {1390 //Don't consume trailing commas as valid 1391 soFar = soFar.slice( match[0].length ) ||soFar;1392 }1393 groups.push( tokens =[] );1394 }1395 1396 matched = false;1397 1398 //关系选择器 1399 if ( (match =rcombinators.exec( soFar )) ) {1400 matched =match.shift();1401 tokens.push( {1402 value: matched,1403 type: match[0].replace( rtrim, " ")1404 } );1405 soFar =soFar.slice( matched.length );1406 }1407 1408 //过滤器 1409 for ( type inExpr.filter ) {1410 //这里如果已匹配上是不是退出for等循环效率高点????tokenize函数要不要写下(把rcomma、rcombinators、其它选择器写到一个for循环) 1411 if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || 1412 (match =preFilters[ type ]( match ))) ) {1413 matched = match.shift();//真正的选择器 1414 tokens.push( {1415 value: matched,//真正的选择器 1416 type: type,//选择器类型 1417 matches: match//选择器的各个部分如:attr ["[name="username"]", "name", "=", "username"] 1418 } );1419 soFar = soFar.slice( matched.length );//截取当前匹配的选择器 1420 }1421 }1422 1423 if ( !matched ) {1424 break;1425 }1426 }1427 1428 //如果我们仅仅解析返回无效的长度,否则返回解析完的选择器对象或者抛出错误 1429 return parseOnly ? 1430 soFar.length :1431 soFar ? 1432 Sizzle.error( selector ) :1433 //Cache the tokens 1434 //缓存解析完的选择器 1435 tokenCache( selector, groups ).slice( 0 );//拷贝缓存中的,因为是引用类型如果直接使用会改变缓存中的值 1436 }1437 1438 functiontoSelector( tokens ) {1439 var i = 0,1440 len =tokens.length,1441 selector = "";1442 for ( ; i < len; i++) {1443 selector +=tokens[i].value;1444 }1445 returnselector;1446 }1447 1448 //根据关系选择器检查 1449 functionaddCombinator( matcher, combinator, base ) {1450 var dir =combinator.dir,1451 checkNonElements = base && dir === "parentNode",1452 doneName = done++;//第几个关系选择器 1453 console.log('遇到第' + doneName + '个关系选择器:');1454 return combinator.first ? 1455 //Check against closest ancestor/preceding element 1456 //检查最靠近的祖先元素 1457 function( elem, context, xml ) {1458 while ( (elem =elem[ dir ]) ) {1459 if ( elem.nodeType === 1 ||checkNonElements ) {1460 returnmatcher( elem, context, xml );1461 }1462 }1463 } :1464 1465 //Check against all ancestor/preceding elements 1466 //检查最靠近的祖先元素或兄弟元素(概据>、~、+还有空格检查) 1467 function( elem, context, xml ) {1468 vardata, cache, outerCache,1469 dirkey = dirruns + " " +doneName;1470 1471 //我们不可以在xml节点上设置任意数据,所以它们不会从dir缓存中受益 1472 if( xml ) {1473 while ( (elem =elem[ dir ]) ) {1474 if ( elem.nodeType === 1 ||checkNonElements ) {1475 if( matcher( elem, context, xml ) ) {1476 return true;1477 }1478 }1479 }1480 } else{1481 while ( (elem =elem[ dir ]) ) {1482 if ( elem.nodeType === 1 ||checkNonElements ) {1483 outerCache = elem[ expando ] || (elem[ expando ] ={});1484 //如果有缓存且符合下列条件则不用再次调用matcher函数 1485 if ( (cache = outerCache[ dir ]) && cache[0] ===dirkey ) {1486 if ( (data = cache[1]) === true || data ===cachedruns ) {1487 return data === true;1488 }1489 } else{1490 cache = outerCache[ dir ] =[ dirkey ];1491 cache[1] = matcher( elem, context, xml ) || cachedruns;//cachedruns//正在匹配第几个元素 1492 if ( cache[1] === true) {1493 return true;1494 }1495 }1496 }1497 }1498 }1499 };1500 }1501 1502 functionelementMatcher( matchers ) {1503 return matchers.length > 1 ? 1504 function( elem, context, xml ) {1505 //这里代码改为下面代码方便调试 1506 //var i = matchers.length; 1507 //while ( i-- ) { 1508 //if ( !matchers[i]( elem, context, xml ) ) { 1509 //return false; 1510 //} 1511 //} 1512 var i =matchers.length;1513 var matcher = null;1514 while ( i--) {1515 matcher =matchers[i];1516 //console.log(matcher) 1517 if ( !matcher( elem, context, xml ) ) {1518 return false;1519 }1520 }1521 return true;1522 } :1523 matchers[0];1524 }1525 1526 functioncondense( unmatched, map, filter, context, xml ) {1527 varelem,1528 newUnmatched =[],1529 i = 0,1530 len =unmatched.length,1531 mapped = map != null;1532 1533 for ( ; i < len; i++) {1534 if ( (elem =unmatched[i]) ) {1535 if ( !filter ||filter( elem, context, xml ) ) {1536 newUnmatched.push( elem );1537 if( mapped ) {1538 map.push( i );1539 }1540 }1541 }1542 }1543 1544 returnnewUnmatched;1545 }1546 1547 //如:'\#test a input[name="username"]:last[type="text"] [value*=2]'(不建议写这样的选择器) 1548 //preFilter位置伪类之前的所有选择器过滤函数 如:'a input[name="username"]'选择器的匹配函数 1549 //selector 位置伪类之前的所有选择器 如:'a input[name="username"]' 1550 //matcher 位置伪类匹配函数 1551 //postFilter如果位置伪类后面还有选择器, 且关系选择器之前(如果有的话) 此选择器的匹配函数 1552 //postFinder如果位置伪类后面还有关系选择器,第一个关系选择器以后的匹配函数 1553 //postSelector如果位置伪类后面还有关系选择器,第一个关系选择器以后的选择器 如:' [value*=2]' 1554 functionsetMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {1555 if ( postFilter && !postFilter[ expando ] ) {1556 postFilter =setMatcher( postFilter );1557 }1558 if ( postFinder && !postFinder[ expando ] ) {1559 postFinder =setMatcher( postFinder, postSelector );1560 }1561 return markFunction(function( seed, results, context, xml ) {//得到位置伪类之前的选择器的元素后在筛选出符合条件伪类选择器的元素 1562 vartemp, i, elem,1563 preMap =[],1564 postMap =[],1565 preexisting =results.length,1566 1567 //根据把位置伪类前面的选择器查找出元素然后再筛选位置伪类和它后面的选择器 1568 elems = seed || multipleContexts( selector || "*", context.nodeType ?[ context ] : context, [] ),1569 1570 //Prefilter to get matcher input, preserving a map for seed-results synchronization 1571 matcherIn = preFilter && ( seed || !selector ) ? 1572 condense( elems, preMap, preFilter, context, xml ) :1573 elems,1574 1575 matcherOut = matcher ? 1576 //If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, 1577 //如果postFinder存在,或者筛选的种子集存在,再或者筛选的种子集不存在但是postFilter存在或results中有值 1578 postFinder || ( seed ? preFilter : preexisting || postFilter ) ? 1579 1580 //...intermediate processing is necessary必须中间处理 1581 [] :1582 1583 //...otherwise use results directly否则立即用resluts 1584 results :1585 matcherIn;1586 1587 //Find primary matches 1588 if( matcher ) {1589 matcher( matcherIn, matcherOut, context, xml );1590 }1591 1592 //Apply postFilter 1593 if( postFilter ) {1594 temp =condense( matcherOut, postMap );1595 postFilter( temp, [], context, xml );1596 1597 //Un-match failing elements by moving them back to matcherIn 1598 //把没有匹配失败的元素放到matcherIn中 1599 i =temp.length;1600 while ( i--) {1601 if ( (elem =temp[i]) ) {1602 matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] =elem);1603 }1604 }1605 }1606 1607 if( seed ) {1608 if ( postFinder ||preFilter ) {1609 if( postFinder ) {1610 //Get the final matcherOut by condensing this intermediate into postFinder contexts 1611 temp =[];1612 i =matcherOut.length;1613 while ( i--) {1614 if ( (elem =matcherOut[i]) ) {1615 //Restore matcherIn since elem is not yet a final match 1616 temp.push( (matcherIn[i] =elem) );1617 }1618 }1619 postFinder( null, (matcherOut =[]), temp, xml );1620 }1621 1622 //Move matched elements from seed to results to keep them synchronized 1623 i =matcherOut.length;1624 while ( i--) {1625 if ( (elem = matcherOut[i]) && 1626 (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1) {1627 1628 seed[temp] = !(results[temp] =elem);1629 }1630 }1631 }1632 1633 //Add elements to results, through postFinder if defined 1634 } else{1635 matcherOut =condense(1636 matcherOut === results ? 1637 matcherOut.splice( preexisting, matcherOut.length ) :1638 matcherOut1639 );1640 if( postFinder ) {1641 postFinder( null, results, matcherOut, xml );1642 } else{1643 push.apply( results, matcherOut );1644 }1645 }1646 });1647 }1648 1649 functionmatcherFromTokens( tokens ) {1650 varcheckContext, matcher, j,1651 len =tokens.length,1652 leadingRelative = Expr.relative[ tokens[0].type ],1653 implicitRelative = leadingRelative || Expr.relative[" "],1654 i = leadingRelative ? 1 : 0,1655 1656 //确保这些元素可以在context中找到 1657 matchContext = addCombinator( function( elem ) {1658 return elem ===checkContext;1659 }, implicitRelative, true),1660 matchAnyContext = addCombinator( function( elem ) {1661 return indexOf.call( checkContext, elem ) > -1;1662 }, implicitRelative, true),1663 //这里用来确定元素在哪个context 1664 matchers = [ function( elem, context, xml ) {1665 return ( !leadingRelative && ( xml || context !== outermostContext ) ) ||(1666 (checkContext = context).nodeType ? 1667 matchContext( elem, context, xml ) :1668 matchAnyContext( elem, context, xml ) );1669 } ];1670 1671 for ( ; i < len; i++) {1672 if ( (matcher =Expr.relative[ tokens[i].type ]) ) {1673 //当遇到关系选择器时elementMatcher函数将matchers数组中的函数生成一个函数(elementMatcher利用了闭包所以matchers一直存在内存中) 1674 matchers =[ addCombinator(elementMatcher( matchers ), matcher) ];1675 } else{1676 matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );//apply方式调用函数, tokens[i].matches为参数 1677 1678 //返回一个特殊的位置匹配函数 1679 //伪类会把selector分两部分 1680 if( matcher[ expando ] ) {1681 //发现下一个关系操作符(如果有话)并做适当处理 1682 j = ++i;1683 for ( ; j < len; j++) {1684 if ( Expr.relative[ tokens[j].type ] ) {//如果位置伪类后面还有关系选择器还需要筛选 1685 break;1686 }1687 }1688 returnsetMatcher(1689 i > 1 &&elementMatcher( matchers ),1690 i > 1 && toSelector( tokens.slice( 0, i - 1 ) ).replace( rtrim, "$1"),1691 matcher,1692 i < j && matcherFromTokens( tokens.slice( i, j ) ),//如果位置伪类后面还有选择器需要筛选 1693 j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),//如果位置伪类后面还有关系选择器还需要筛选 1694 j < len &&toSelector( tokens )1695 );1696 }1697 matchers.push( matcher );1698 }1699 }1700 1701 returnelementMatcher( matchers );1702 }1703 1704 functionmatcherFromGroupMatchers( elementMatchers, setMatchers ) {1705 //A counter to specify which element is currently being matched 1706 //用计数器来指定当前哪个元素正在匹配 1707 var matcherCachedRuns = 0,1708 bySet = setMatchers.length > 0,1709 byElement = elementMatchers.length > 0,1710 superMatcher = function( seed, context, xml, results, expandContext ) {1711 varelem, j, matcher,1712 setMatched =[],1713 matchedCount = 0,1714 i = "0",1715 unmatched = seed &&[],1716 outermost = expandContext != null,1717 contextBackup =outermostContext,1718 //We must always have either seed elements or context 1719 //我们必须总是检查seed中的元素或context下的所有元素 1720 elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode ||context ),1721 //Use integer dirruns iff this is the outermost matcher 1722 //用整数dirruns区分这个是最外层的匹配函数 1723 dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1);1724 if( outermost ) {1725 outermostContext = context !== document &&context;1726 cachedruns =matcherCachedRuns;1727 }1728 1729 //通过elementMatchers内的所有匹配函数的元素立即添加到results中 1730 //保持变量i是一个字符串,如果一个元素也没有下面的元素匹配数量matchedCount为'00' 1731 for ( ; (elem = elems[i]) != null; i++) {1732 if ( byElement &&elem ) {1733 j = 0;1734 while ( (matcher = elementMatchers[j++]) ) {1735 if( matcher( elem, context, xml ) ) {1736 results.push( elem );1737 break;1738 }1739 }1740 if( outermost ) {1741 dirruns =dirrunsUnique;1742 cachedruns = ++matcherCachedRuns;//正在匹配第几个元素(同时告诉全局的,这里的全局是sizzle内的不是window的) 1743 }1744 }1745 1746 //Track unmatched elements for set filters 1747 //跟踪不匹配元素并设置过滤 1748 if( bySet ) {1749 //They will have gone through all possible matchers 1750 //它们已经通过所有的匹配器,如果元素不匹配matchedCount减1 1751 if ( (elem = !matcher &&elem) ) {1752 matchedCount--;1753 }1754 1755 //Lengthen the array for every element, matched or not 1756 //如果seed中有元素的话,不管是否匹配都要把每个元素放到一个延长数组中 1757 if( seed ) {1758 unmatched.push( elem );1759 }1760 }1761 }1762 1763 //Apply set filters to unmatched elements 1764 //用设置的过滤器来去除不匹配的元素 1765 matchedCount += i;//i是所有元素数量,matchedCount之前是不匹配的元素数量,所以matchedCount += i就是匹配的数量 1766 if ( bySet && i !==matchedCount ) {1767 j = 0;1768 while ( (matcher = setMatchers[j++]) ) {1769 //这里筛选unmatched内的元素 1770 matcher( unmatched, setMatched, context, xml );//(这里的setMatched是引用类型,所以函数matcher内给setMatched添加的元素和这里的setMatched一样 1771 }1772 1773 if( seed ) {1774 //Reintegrate element matches to eliminate the need for sorting 1775 if ( matchedCount > 0) {1776 while ( i--) {1777 if ( !(unmatched[i] ||setMatched[i]) ) {1778 setMatched[i] =pop.call( results );1779 }1780 }1781 }1782 1783 //Discard index placeholder values to get only actual matches 1784 setMatched =condense( setMatched );1785 }1786 1787 //Add matches to results 1788 //添加匹配元素到results 1789 push.apply( results, setMatched );1790 1791 //并联选择器匹配成功后按规定排序 1792 if ( outermost && !seed && setMatched.length > 0 && 1793 ( matchedCount + setMatchers.length ) > 1) {1794 1795 Sizzle.uniqueSort( results );1796 }1797 }1798 1799 //Override manipulation of globals by nested matchers 1800 if( outermost ) {1801 dirruns =dirrunsUnique;1802 outermostContext =contextBackup;1803 }1804 1805 returnunmatched;1806 };1807 1808 return bySet ? 1809 markFunction( superMatcher ) :1810 superMatcher;1811 }1812 1813 compile = Sizzle.compile = function( selector, group /*Internal Use Only*/) {1814 vari,1815 setMatchers =[],1816 elementMatchers =[],1817 cached = compilerCache[ selector + " "];1818 1819 if ( !cached ) {1820 //生成一个递归函数用来检查每个元素 1821 if ( !group ) {1822 group =tokenize( selector );1823 }1824 i =group.length;1825 while ( i-- ) {//如果是有并联选择器这里多次等循环 1826 cached =matcherFromTokens( group[i] );1827 if ( cached[ expando ] ) {//说明有位置伪类选择器 1828 setMatchers.push( cached );1829 } else{1830 elementMatchers.push( cached );1831 }1832 }1833 1834 //当是并联选择器时(也就是group数组有多个元素),elementMatchers和setMatchers可能都有值 1835 cached =compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );1836 }1837 returncached;1838 };1839 1840 functionmultipleContexts( selector, contexts, results ) {1841 var i = 0,1842 len =contexts.length;1843 for ( ; i < len; i++) {1844 Sizzle( selector, contexts[i], results );1845 }1846 returnresults;1847 }1848 1849 functionselect( selector, context, results, seed ) {1850 vari, tokens, token, type, find,1851 match = tokenize( selector );//把selector解析成一个个独立的块 1852 1853 if ( !seed ) {1854 //如果不是并联选择器则尽量减少操作 1855 if ( match.length === 1) {1856 1857 //如果第一个是selector是id我们可以设置context快速查找 1858 tokens = match[0] = match[0].slice( 0);1859 if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && 1860 context.nodeType === 9 && documentIsHTML && 1861 Expr.relative[ tokens[1].type ] ) {1862 1863 context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];1864 if ( !context ) {//如果context这个元素(selector第一个id选择器)都不存在就不用查找了 1865 returnresults;1866 }1867 1868 selector = selector.slice( tokens.shift().value.length );//去掉第一个id选择器 1869 }1870 1871 //如果selector中没有有位置伪类从右向左匹配 1872 i = matchExpr["needsContext"].test( selector ) ? 0: tokens.length;1873 while ( i--) {1874 token =tokens[i];1875 1876 //如果遇到了关系选择器中止 1877 if ( Expr.relative[ (type =token.type) ] ) {1878 break;1879 }1880 if ( (find =Expr.find[ type ]) ) {1881 //如果selector第一个选择器是兄弟选择器,则扩大context范围(原因要查找的节点在这里必须是它的父节点或祖先节点) 1882 if ( (seed =find(1883 token.matches[0].replace( runescape, funescape ),1884 rsibling.test( tokens[0].type ) && context.parentNode ||context1885 )) ) {1886 1887 //如果seed是空的或者tokens中没有值了,就可以return了没有必要查找了 1888 tokens.splice( i, 1);1889 selector = seed.length &&toSelector( tokens );1890 if ( !selector ) {1891 push.apply( results, seed );1892 returnresults;1893 }1894 1895 break;1896 }1897 }1898 }1899 }1900 }1901 1902 //Compile and execute a filtering function 1903 //Provide `match` to avoid retokenization if we modified the selector above 1904 //编译并执行过滤函数 1905 //上面如果修改了selector,match同样修改,我们把match传过去可以避免compile内再次调用tokenize 1906 compile( selector, match )(1907 seed,1908 context,1909 !documentIsHTML,1910 results,1911 rsibling.test( selector )//selector中是否有兄弟关系选择器 1912 );1913 returnresults;1914 }1915 1916 //Deprecated 1917 Expr.pseudos["nth"] = Expr.pseudos["eq"];1918 1919 //Easy API for creating new setFilters 1920 //1921 functionsetFilters() {}1922 1923 setFilters.prototype = Expr.filters =Expr.pseudos;1924 Expr.setFilters = newsetFilters();1925 1926 //Check sort stability 1927 support.sortStable = expando.split("").sort( sortOrder ).join("") ===expando;1928 1929 //初始化默认document 1930 setDocument();1931 1932 //Always assume the presence of duplicates if sort doesn't 1933 //pass them to our comparison function (as in Google Chrome). 1934 [0, 0].sort( sortOrder );1935 support.detectDuplicates =hasDuplicate;1936 1937 //EXPOSE 1938 //sizzle对外公开 1939 if ( typeof define === "function" &&define.amd ) {1940 define(function() { returnSizzle; });1941 } else{1942 window.Sizzle =Sizzle;1943 }1944 //EXPOSE 1945 1946 })( window );
部分参考:http://www.cnblogs.com/rubylouvre/archive/2013/03/05/2943666.html
转载于:https://www.cnblogs.com/daycool/archive/2013/04/15/3023169.html
jquery--选择器sizzle源码分析相关推荐
- jQuery-1.9.1源码分析系列(二)jQuery选择器续2——筛选
前面分析了选择器的结构和几个解析函数,接下来分析jQuery对象的伪类选择器.这里所谓的jQuery对象的伪类选择器就是从已有的jQuery对象(元素集合)中筛选出指定的集合出来. 4. jQu ...
- jQuery源码分析系列
声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...
- [转]jQuery源码分析系列
文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaro ...
- 1号店案例html源码_手把手教一起写jQuery版mini源码,分析jQuery的优势
适合人群 本文适合0.5~3年的前端开发人员,以及想了解jQuery是什么的小伙伴们. 前言 谈谈个人对jQuery的看法. 如果你是一个五年以上的开发人员,相信你一定认识了解jQuery.这好比你十 ...
- jQuery源码分析-10事件处理-Event-事件绑定与删除-bind/unbind+live/die+delegat/unde
10.4 .bind() .one() 10.4.1 如何使用 .bind( eventType, [eventData], handler(eventObject) ) 在匹配的元素上绑 ...
- jQuery源码分析-10事件处理-Event-事件绑定与删除-bind/unbind+live/die+delegat/undelegate
Js代码 作者:nuysoft/高云 QQ:47214707 EMail:nuysoft@gmail.com 声明:本文为原创文章,如需转载,请注明来源并保留原文链接. 后文预告:封装事件对象 便 ...
- jQuery源码分析之实例find和filter方法的区别七问
问题1:jQuery.filter的源码是什么? jQuery.filter = function( expr, elems, not ) {var elem = elems[ 0 ];//如果含有第 ...
- jQuery 2.0.3 源码分析 Deferred(最细的实现剖析,带图)
Deferred的概念请看第一篇 http://www.cnblogs.com/aaronjs/p/3348569.html ******************构建Deferred对象时候的流程图* ...
- jQuery 源码分析第一篇之入口源码
目前阅读的是jQuery 1.11.3的源码,有参考nuysoft的资料.原来比较喜欢在自己的Evernote上做学习基类,并没有在网上写技术博客的习惯,现在开始学习JS的开源代码,想跟大家多交流,希 ...
最新文章
- shell学习笔记 (2)
- 准确理解 Precision 准确率, Recall 召回率 , IoU
- java socket nio 阻塞_Java NIO实现非阻塞式socket通信
- EasyUI中放置Droppable的使用以及实现拖拽排序
- vs五子棋c语言代码,五子棋代码C语言版.doc
- win2003系统网络安装——基于linux+pxe+dhcp+tftp+samba+ris
- android服务应用场景,Android Service的使用介绍
- mysql中union与union all的区别
- 【C++深度剖析教程2】C++经典问题解析之二 this指针与成员函数
- php imagick手册,PHP中使用Imagick实现各种图片效果实例
- 计算机组成原理r型指令logisim实现_第一章 计算机体系结构
- 清华博士庞天宇90页的PPT分享,如何让AI模型更皮实,更稳定?(精彩直播回放)...
- 问答 请问使用OK(raw:jpg)能返回多张图片吗
- zabbix安装部署_听说你的学习之路又停留在了“不会安装”
- HDOJ水题集合11:桶排序, 折半搜索
- 2021-08-13 初识servlet
- NZ源码交易平台虚拟交易系统(商家版) 高仿淘码网模板
- java编译过程_Java编译运行过程
- JWT 帮助类 JWTHelper
- 手机总是显示服务器太忙,手机总提示服务器太忙请稍后重试
热门文章
- nignx处理Html中SSI技术代码注意事项
- 1bit和1byte_1byte等于( )bit_学小易找答案
- 腐蚀rust研究台抽奖_中石化青岛安工院专家分享延迟焦化装置的腐蚀风险分析!...
- Telnet 爆破 kail_【UZI|SN输给DWG后,AD选手被爆破,弹幕刷了半小时Uzi】英雄联盟S10于10月31日终于正式落幕了_科技资讯...
- gin context和官方context_Go语言gin框架从入门到精通(3)
- java 找不到构造函数_JAVA找不到符号构造函数
- Zabbix 3.0 部署监控 [二]
- 互联网晚报 | 9月5日 星期日 | 美菜回应大规模裁员;网易云音乐Q2毛利率首次转正;美团展示数字人民币新应用...
- 互联网日报 | 京东开启最大规模校招;特斯拉西部首个交付中心在蓉投入使用;嫦娥五号上升器点火起飞...
- 2020年上半年家电市场报告