不是所有的选择器都需要去分词,生成相应的匹配函数,这样流程比较复杂,当浏览器具备原生的方法去匹配元素是,没有理由不优先匹配,下面看看进入Sizzle后,它是怎么优先匹配这些元素的:
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");
}
}
}
如果成功就返回results