3.1 总体结构
Sizzle的总体源码结构如代码清单3-1所示,为了方便解释,代码中增加了注释:
代码清单3-1 Sizzle 的总体源码结构
(function(){
// 选择器引擎入口,查找与选择器表达式 selector 匹配的元素集合
var Sizzle = function( selector, context, results, seed ) { ... };
// 工具方法,排序、去重
Sizzle.uniqueSort = function( results ) { ... };
// 便捷方法,使用指定的选择器表达式 expr 对元素集合 set 进行过滤
Sizzle.matches = function( expr, set ) { ... };
// 便捷方法,检查某个元素 node 是否匹配选择器表达式 expr
Sizzle.matchesSelector = function( node, expr ) { ... };
// 内部方法,对块表达式进行查找
Sizzle.find = function( expr, context, isXML ) { ... };
// 内部方法,用块表达式过滤元素集合
Sizzle.filter = function( expr, set, inplace, not ) { ... };
// 工具方法,抛出异常
Sizzle.error = function( msg ) { ... };
// 工具方法,获取 DOM 元素集合的文本内容
var getText = Sizzle.getText = function( elem ) { ... };
// 扩展方法和属性
var Expr = Sizzle.selectors = {
// 块表达式查找顺序
order: [ "ID", "NAME", "TAG" ],
// 正则表达式集,用于匹配和解析块表达式
match: { ID, CLASS, NAME, ATTR, TAG, CHILD, POS, PSEUDO },
leftMatch: { ... },
// 属性名修正函数集
attrMap: { "class", "for" },
// 属性值读取函数集
attrHandle: { href, type },
// 块间关系过滤函数集
relative: { "+", ">", "", "~" },
// 块表达式查找函数集
find: { ID, NAME, TAG },
// 块表达式预过滤函数集
preFilter: { CLASS, ID, TAG, CHILD, ATTR, PSEUDO, POS },
// 伪类过滤函数集
filters: { enabled, disabled, checked, selected, parent, empty, has, header,
text, radio, checkbox, file, password, submit, image, reset, button, input,
focus },
// 位置伪类过滤函数集
setFilters: { first, last, even, odd, lt, gt, nth, eq },
// 块表达式过滤函数集
filter: { PSEUDO, CHILD, ID, TAG, CLASS, ATTR, POS }
};
// 如果支持方法 querySelectorAll(),则调用该方法查找元素
if ( document.querySelectorAll ) {
(function(){
var oldSizzle = Sizzle;
Sizzle = function( query, context, extra, seed ) {
// 尝试调用方法 querySelectorAll() 查找元素
// 如果上下文是 document,则直接调用 querySelectorAll() 查找元素
return makeArray( context.querySelectorAll(query), extra );
// 如果上下文是元素,则为选择器表达式增加上下文,然后调用 querySelectorAll()
// 查找元素
return makeArray( context.querySelectorAll( "[id='" + nid + "'] " +
query ), extra );
// 如果查找失败,则仍然调用 oldSizzle()
return oldSizzle(query, context, extra, seed);
};
})();
}
// 如果支持方法 matchesSelector(),则调用该方法检查元素是否匹配选择器表达式
(function(){
var matches = html.matchesSelector
|| html.mozMatchesSelector
|| html.webkitMatchesSelector
|| html.msMatchesSelector;
// 如果支持方法 matchesSelector()
if ( matches ) {
Sizzle.matchesSelector = function( node, expr ) {
// 尝试调用方法 matchesSelector()
var ret = matches.call( node, expr );
return ret;
// 如果查找失败,则仍然调用 Sizzle()
return Sizzle(expr, null, null, [node]).length > 0;
};
}
})();
// 检测浏览器是否支持 getElementsByClassName()
(function(){
Expr.order.splice(1, 0, "CLASS");
Expr.find.CLASS = function( match, context, isXML ) { ... };
})();
// 工具方法,检测元素 a 是否包含元素 b
Sizzle.contains = function( a, b ) { ... };
})();
代码清单3-1中的变量Expr与Sizzle.selectors指向了同一个对象,这么做是为了减少拼写字符数、缩短作用域链,并且方便压缩。但是为了直观和避免混淆,本章在描述中统一使用Sizzle.selectors。
代码清单3-1中已经介绍了浏览器支持方法querySelectorAll()时的查找过程,本章后面的内容将介绍和分析在不支持的情况下,Sizzle是如何模拟方法querySelectorAll()的行为的。另外,为了简化描述,在后文中把“块表达式查找函数集”“块表达式预过滤函数集”“块表达式过滤函数集”分别简称为“查找函数集”“预过滤函数集”“过滤函数集”。
代码清单3-1中的方法和属性大致可以分为4类:公开方法、内部方法、工具方法、扩展方法及属性。它们之间的调用关系如图3-1所示。
图3-1 Sizzle的方法、功能和调用关系