日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

jquery--选择器sizzle源码分析

發布時間:2025/3/15 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 jquery--选择器sizzle源码分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
1 (function( window, undefined ) { 2 3 var i, 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" + -(new Date()), 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 = typeof undefined, 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 return i; 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 = new RegExp( 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.childNodes 137 ); 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 function isNative( fn ) { 163 return rnative.test( fn + "" ); 164 } 165 166 //創建緩存函數(緩存大小默認為50,可以自己設置) 167 //這里利用了閉包(私有變量、變量一直保存在內存中) 168 function createCache() { 169 var cache, 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 delete cache[ keys.shift() ]; 177 } 178 return (cache[ key ] = value); 179 }); 180 } 181 182 //標記函數供sizzle特殊用途 183 function markFunction( fn ) { 184 fn[ expando ] = true; 185 return fn; 186 } 187 188 // 用于做各種特征檢測,比如是否支持某個API,API支持是否完美 189 190 function assert( 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 function Sizzle( selector, context, results, seed ) { 207 var match, 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 return results; 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 return results; 243 } 244 } else { 245 return results; 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 return results; 253 } 254 } 255 256 // Speed-up: Sizzle("TAG") 257 } else if ( match[2] ) { 258 push.apply( results, context.getElementsByTagName( selector ) ); 259 return results; 260 261 // Speed-up: Sizzle(".CLASS") 262 } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { 263 push.apply( results, context.getElementsByClassName( m ) ); 264 return results; 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 return results; 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 return document; 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 return pass; 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 return context.getElementsByTagName( tag ); 472 } 473 } : 474 function( tag, context ) { 475 var elem, 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 return tmp; 489 } 490 return results; 491 }; 492 493 //如果支持根據name查找節點 494 Expr.find["NAME"] = support.getByName && function( tag, context ) { 495 if ( typeof context.getElementsByName !== strundefined ) { 496 return context.getElementsByName( name ); 497 } 498 }; 499 500 //如果支持根據class查找節點 501 Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { 502 if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) { 503 return context.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 var cur, 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 return siblingCheck( 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 return document; 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 return ret; 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 return contains( context, elem ); 727 }; 728 729 Sizzle.attr = function( elem, name ) { 730 var val; 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 return val( elem ); 742 } 743 if ( !documentIsHTML || support.attributes ) { 744 return elem.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 var elem, 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 return results; 782 }; 783 784 // 兄弟節點排序 785 786 function siblingCheck( 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 return diff; 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 function createInputPseudo( 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 function createButtonPseudo( 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 function createPositionalPseudo( fn ) { 829 return markFunction(function( argument ) { 830 argument = +argument; 831 return markFunction(function( seed, matches ) { 832 var j, 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 var node, 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 return elem.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 return elem.nodeValue; 872 } 873 // Do not include comment or processing instruction nodes 874 875 return ret; 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-component 919 6 x of xn-component 920 7 sign of y-component 921 8 y of y-component 922 */ 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 return match; 949 }, 950 951 "PSEUDO": function( match ) { 952 var excess, 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 var cache, 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 var args, 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 return fn( 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 var idx, 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 return fn; 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 var elem, 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 var elemLang; 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 return rheader.test( elem.nodeName ); 1296 }, 1297 1298 "input": function( elem ) { 1299 return rinputs.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 var attr; 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 return matchIndexes; 1335 }), 1336 1337 "odd": createPositionalPseudo(function( matchIndexes, length ) { 1338 var i = 1; 1339 for ( ; i < length; i += 2 ) { 1340 matchIndexes.push( i ); 1341 } 1342 return matchIndexes; 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 return matchIndexes; 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 return matchIndexes; 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 function tokenize( selector, parseOnly ) { 1373 var matched, 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 in Expr.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 function toSelector( tokens ) { 1439 var i = 0, 1440 len = tokens.length, 1441 selector = ""; 1442 for ( ; i < len; i++ ) { 1443 selector += tokens[i].value; 1444 } 1445 return selector; 1446 } 1447 1448 //根據關系選擇器檢查 1449 function addCombinator( 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 return matcher( elem, context, xml ); 1461 } 1462 } 1463 } : 1464 1465 // Check against all ancestor/preceding elements 1466 //檢查最靠近的祖先元素或兄弟元素(概據>、~、+還有空格檢查) 1467 function( elem, context, xml ) { 1468 var data, 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 function elementMatcher( 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 function condense( unmatched, map, filter, context, xml ) { 1527 var elem, 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 return newUnmatched; 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 function setMatcher( 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 var temp, 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 matcherOut 1639 ); 1640 if ( postFinder ) { 1641 postFinder( null, results, matcherOut, xml ); 1642 } else { 1643 push.apply( results, matcherOut ); 1644 } 1645 } 1646 }); 1647 } 1648 1649 function matcherFromTokens( tokens ) { 1650 var checkContext, 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 return setMatcher( 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 return elementMatcher( matchers ); 1702 } 1703 1704 function matcherFromGroupMatchers( 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 var elem, 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 return unmatched; 1806 }; 1807 1808 return bySet ? 1809 markFunction( superMatcher ) : 1810 superMatcher; 1811 } 1812 1813 compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { 1814 var i, 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 return cached; 1838 }; 1839 1840 function multipleContexts( selector, contexts, results ) { 1841 var i = 0, 1842 len = contexts.length; 1843 for ( ; i < len; i++ ) { 1844 Sizzle( selector, contexts[i], results ); 1845 } 1846 return results; 1847 } 1848 1849 function select( selector, context, results, seed ) { 1850 var i, 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 return results; 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 || context 1885 )) ) { 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 return results; 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 return results; 1914 } 1915 1916 // Deprecated 1917 Expr.pseudos["nth"] = Expr.pseudos["eq"]; 1918 1919 // Easy API for creating new setFilters 1920 // 1921 function setFilters() {} 1922 1923 setFilters.prototype = Expr.filters = Expr.pseudos; 1924 Expr.setFilters = new setFilters(); 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() { return Sizzle; }); 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源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。