不是全部的選擇器都須要去分詞,生成相應的匹配函數,這樣流程比較複雜,當瀏覽器具有原生的方法去匹配元素是,沒有理由不優先匹配,下面看看進入Sizzle後,它是怎麼優先匹配這些元素的:node
function Sizzle( selector, context, results, seed ) { /* 執行$("ul.list>li span:eq(1)")時,遞歸第二次時, selector "ul.list>li span" */ var match, elem, m, nodeType, // QSA vars i, groups, old, nid, newContext, newSelector; if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { setDocument( context ); } context = context || document; results = results || []; if ( !selector || typeof selector !== "string" ) { return results; } if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { return []; } if ( documentIsHTML && !seed ) { // Shortcuts if ( (match = rquickExpr.exec( selector )) ) {//先看是否是簡單的ID選擇器 TAG選擇器 類選擇器 // Speed-up: Sizzle("#ID") if ( (m = match[1]) ) { if ( nodeType === 9 ) { elem = context.getElementById( m ); // Check parentNode to catch when Blackberry 4.6 returns // nodes that are no longer in the document #6963 if ( elem && elem.parentNode ) { // Handle the case where IE, Opera, and Webkit return items // by name instead of ID if ( elem.id === m ) { results.push( elem ); return results; } } else { return results; } } else { // Context is not a document if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && contains( context, elem ) && elem.id === m ) { results.push( elem ); return results; } } // Speed-up: Sizzle("TAG") } else if ( match[2] ) { push.apply( results, context.getElementsByTagName( selector ) ); return results; // Speed-up: Sizzle(".CLASS") } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { push.apply( results, context.getElementsByClassName( m ) ); return results; } } // QSA path 是否支持document.querySelectorAll if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { nid = old = expando; newContext = context; newSelector = nodeType === 9 && selector; // qSA works strangely on Element-rooted queries // We can work around this by specifying an extra ID on the root // and working up from there (Thanks to Andrew Dupont for the technique) // IE 8 doesn't work on object elements if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { groups = tokenize( selector ); if ( (old = context.getAttribute("id")) ) { nid = old.replace( rescape, "\\$&" ); } else { context.setAttribute( "id", nid ); } nid = "[id='" + nid + "'] "; i = groups.length; while ( i-- ) { groups[i] = nid + toSelector( groups[i] ); } newContext = rsibling.test( selector ) && context.parentNode || context; newSelector = groups.join(","); } if ( newSelector ) { try { push.apply( results, newContext.querySelectorAll( newSelector ) ); return results; } catch(qsaError) { } finally { if ( !old ) { context.removeAttribute("id"); } } } } } // All others //去除選擇器首位空格,並進入select函數 return select( selector.replace( rtrim, "$1" ), context, results, seed ); }
先看:rquickExpr數組
rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
當執行:match = rquickExpr.exec( selector ),產生一個長度爲4的數組:其中match[1]ID匹配項,match[2]爲tag匹配項,match[3]爲類匹配項
好比:當執行$("div")時,
match=["div", undefined, "div", undefined]
接下來就判斷具體符合哪一種選擇器了,而後用原生方法直接得到。
若是不是上述三種選擇器,那麼開始試探querySelectorAll:
// QSA path 是否支持document.querySelectorAll if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
而後就就開始try嘗試:瀏覽器
if ( newSelector ) { try { push.apply( results, newContext.querySelectorAll( newSelector ) ); return results; } catch(qsaError) { } finally { if ( !old ) { context.removeAttribute("id"); } } }
若是成功就返回resultsapp