对jQuery的Sizzle各方法做了深入分析(同时也参考了一些网上资料)后,将结果分享给大家。我将采用连载的方式,对Sizzle使用的一些方法详细解释一下,每篇文章介绍一个方法。
若需要转载,请写明出处,多谢。
rush:js;">
/*
* Sizzle方法是Sizzle选择器包的主要入口,jQuery的find方法就是调用该方法获取匹配的节点
* 该方法主要完成下列任务:
* 1、对于单一选择器,且是ID、Tag、Class三种类型之一,则直接获取并返回结果
* 2、对于支持querySelectorAll方法的浏览器,通过执行querySelectorAll方法获取并返回匹配的DOM元素
* 3、除上之外则调用select方法获取并返回匹配的DOM元素
*
*
* @param selector 选择器字符串
* @param context 执行匹配的最初的上下文(即DOM元素集合)。若context没有赋值,则取document。
* @param results 已匹配出的部分最终结果。若results没有赋值,则赋予空数组。
* @param seed 初始集合
*/
function Sizzle(selector,context,results,seed) {
var match,elem,m,nodeType,// QSA vars
i,groups,old,nid,newContext,newSelector;
/*
* preferredDoc = window.document
*
* setDocument<a href="https://www.jb51.cc/tag/fangfa/" target="_blank" class="keywords">方法</a>完成一些初始化工作
*/
if ((context ? context.ownerDocument || context : preferredDoc) !== document) {
setDocument(context);
}
context = context || document;
results = results || [];
/*
* 若selector不是有效地字符串类型数据,则直接返回results
*/
if (!selector || typeof selector !== "string") {
return results;
}
/*
* 若context既不是document(nodeType=9),也不是element(nodeType=1),那么就返回空集合
*/
if ((nodeType = context.nodeType) !== 1 && nodeType !== 9) {
return [];
}
// 若当前过滤的是HTML文档,且没有设定seed,则执行if内的语句体
if (documentIsHTML && !seed) {
/*
* 若选择器是单一选择器,且是ID、Tag、Class三种类型之一,则直接<a href="https://www.jb51.cc/tag/huoqu/" target="_blank" class="keywords">获取</a>并返回结果
*
* rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/
* 上述正则表达式括号内三段依次分别用来判断是否是ID、TAG、CLASS类型的单一选择器
* 上述正则表达式在最外层圆括号内有三个子表达式(即三个圆括号括起来的部分),
* 分别代表ID、Tag、Class选择器的值,在下面<a href="https://www.jb51.cc/tag/daima/" target="_blank" class="keywords">代码</a>中,分别体现在match[1]、match[2]、match[3]
*/
if ((match = rquickExpr.exec(selector))) {
// Speed-up: Sizzle("#ID")
// 处理ID类型选择器,如:#ID
if ((m = match[1])) {
// 若当前上下文是<a href="https://www.jb51.cc/tag/yige/" target="_blank" class="keywords">一个</a>document,则执行if内语句体
if (nodeType === 9) {
elem = context.getElementById(m);
// Check parentNode to catch when Black<a href="https://www.jb51.cc/tag/Berry/" target="_blank" class="keywords">Berry</a> 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
/*
-
一些老版本的浏览器会把name当作ID来处理,
-
返回不正确的结果,所以需要再一次对比返回节点的ID属性
/
if (elem.id === m) {
results.push(elem);
return results;
}
} else {
return results;
}
} else {
// Context is not a document
/- contains(context,elem)用来确认获取的elem是否是当前context对象的子对象
*/
if (context.ownerDocument
&& (elem = context.ownerDocument.getElementById(m))
&& contains(context,elem) && elem.id === m) {
results.push(elem);
return results;
}
}
// Speed-up: Sizzle("TAG") // 处理Tag类型选择器,如:SPAN } else if (match[2]) { push.apply(results,context.getElementsByTagName(selector)); return results; // Speed-up: Sizzle(".CLASS") /* * 处理class类型选择器,如:.class * 下面<a href="https://www.jb51.cc/tag/tiaojianpanduan/" target="_blank" class="keywords">条件判断</a>分别是: * m = match[3]:有效的class类型选择器 * support.getElementsByClassName 该选择器的div<a href="https://www.jb51.cc/tag/zhichi/" target="_blank" class="keywords">支持</a>getElementsByClassName * context.getElementsByClassName 当前上下文节点有getElementsByClassName<a href="https://www.jb51.cc/tag/fangfa/" target="_blank" class="keywords">方法</a> * */ } else if ((m = match[3]) && support.getElementsByClassName && context.getElementsByClassName) { push.apply(results,context.getElementsByClassName(m)); return results; } } // QSA path /* * 若浏览器<a href="https://www.jb51.cc/tag/zhichi/" target="_blank" class="keywords">支持</a>querySelectorAll<a href="https://www.jb51.cc/tag/fangfa/" target="_blank" class="keywords">方法</a>且选择器符合querySelectorAll<a href="https://www.jb51.cc/tag/diaoyong/" target="_blank" class="keywords">调用</a>标准,则执行if内语句体 * <a href="https://www.jb51.cc/tag/zaizheli/" target="_blank" class="keywords">在这里</a>的检查仅仅是简单匹配 * 第一次<a href="https://www.jb51.cc/tag/diaoyong/" target="_blank" class="keywords">调用</a>Sizzle时,rbuggyQSA为空 * * if语句体内对当前context对象的id的赋值与恢复,是用来修正querySelectorAll的<a href="https://www.jb51.cc/tag/yige/" target="_blank" class="keywords">一个</a>BUG * 该BUG会在某些情况下把当前节点(context)也作为结果返回回来。 * 具体<a href="https://www.jb51.cc/tag/fangfa/" target="_blank" class="keywords">方法</a>是,在现有的选择器前<a href="https://www.jb51.cc/tag/jiashang/" target="_blank" class="keywords">加上</a><a href="https://www.jb51.cc/tag/yige/" target="_blank" class="keywords">一个</a><a href="https://www.jb51.cc/tag/shuxing/" target="_blank" class="keywords">属性</a>选择器:[id=XXX], * XXX 为context的id,若context本身没有设置id,则给个<a href="https://www.jb51.cc/tag/mo/" target="_blank" class="keywords">默</a>认值expando。 */ if (support.qsa && (!rbuggyQSA || !rbuggyQSA.test(selector))) { nid = old = expando; newContext = context; // 若context是document,则newSelector取自selector,否则为false 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"))) { /* * rescape = /'|\\/g,* 这里将old中的单引号、竖杠、反斜杠前加<a href="https://www.jb51.cc/tag/yige/" target="_blank" class="keywords">一个</a>反斜杠 * old.replace(rescape,"\\$&")<a href="https://www.jb51.cc/tag/daima/" target="_blank" class="keywords">代码</a>中的$&代表匹配项 */ nid = old.replace(rescape,"\\$&"); } else { context.setAttribute("id",nid); } nid = "[id='" + nid + "'] "; // 重新组合新的选择器 i = groups.length; while (i--) { groups[i] = nid + toSelector(groups[i]); } /* * rsibling = new RegExp(whitespace + "*[+~]") * rsibling用于判定选择器是否存在兄弟关系符 * 若包含+~符号,则取context的父节点作为当前节点 */ newContext = rsibling.test(selector) && context.parentNode
|| context;
newSelector = groups.join(",");
}if (newSelector) { /* * 这里之所以需要用try...catch, * 是因为jquery所<a href="https://www.jb51.cc/tag/zhichi/" target="_blank" class="keywords">支持</a>的一些选择器是querySelectorAll所<a href="https://www.jb51.cc/tag/buzhichi/" target="_blank" class="keywords">不支持</a>的, * 当使用这些选择器时,querySelectorAll会报非法选择器, * 故需要jquery自身去实现。 */ try { // 将querySelectorAll<a href="https://www.jb51.cc/tag/huoqu/" target="_blank" class="keywords">获取</a>的结果并入results,而后返回resulsts push.apply(results,newContext
.querySelectorAll(newSelector));
return results;
} catch (qSAError) {
} finally {
if (!old) {
context.removeAttribute("id");
}
}
}
}
}// All others
// 除上述快捷方式和调用querySelectorAll方式直接获取结果外,其余都需调用select来获取结果
/*- rtrim = new RegExp("^" + whitespace + "+|((?:^|[^\\])(?:\\.)*)"
-
- whitespace + "+$","g"),* whitespace = "[\x20\t\r\n\f]";
- 上述rtrim正则表达式的作用是去掉selector两边的空白,空白字符由whitespace变量定义
- rtrim的效果与new RegExp("^" + whitespace + "+|" + whitespace + "+$","g")相似
*/
return select(selector.replace(rtrim,"$1"),seed);
}
- contains(context,elem)用来确认获取的elem是否是当前context对象的子对象
各位朋友,若觉得写得不错,帮我顶一下,给点动力,多谢!