这是我参与11月更文挑战的第16天,活动详情查看:2021最后一次更文挑战
1, Sizzle选择器
Sizzle是一款纯JavaScript实现的CSS选择器引擎,它具有一下特性:
- 完全独立,无库依赖
- 相较于大多数常用选择器其性能非常有竞争力
- 压缩和开启gzip后只有4KB
- 具有高扩展性和易于使用的API
- 支持多种浏览器 在Sizzle内部, 如果浏览器支持方法querySelectorAll(), 则调用该方法查找元素, 如果不支持, 则模拟该方法的行为, 在jQuery内部,大多数时候,总是先调用Sizzle查找元素,然后调用jQuery方法对查找结果进行操作。具体的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, it, gt, nth, eq),
filter: (PSEUDO, CHILD, ID< TAG, CLASS, ATTR, POS)
};
// 如果支持方法 querySelectorAll(), 则调用该方法查找元素
if(document.querySelectorAll) {
(function(){
var oldSizzle = Sizzle;
Sizzle = function(query, econtext, extra, seed) {
// 如果上下文是document, 则直接使用querySelectorAll()查找元素
return makeArray(context.querySelectorAll(query), extra);
// 如果上下文是元素, 则为选择器表达式增加上下文, 然后调用querySelectorAll
return makeArray(context, querySelectorAll("[id='" + nid +
"']" + query), extra);
// 如果查找失败,则则仍然调用oldSizzle()
return oldSizzle(query, conetext, extra, seed)
}
})();
}
// 如果支持方法matchesSelector(), 则调用该方法检查元素是否匹配选择器表达式
(function() {
var matches = html.matchesSelector
|| html.mozMathesSelector
|| 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 = functin(a, b) {}
})();
2, 选择器表达式
常用的表达式术语
| 术语 | 说明和实例 |
|---|---|
| 选择器表达式 | CSS选择器表达式,例如: "div>p" |
| 并列选择器表达式 | 逗号分割的多个选择器表达式:例如: "div, p" |
| 块表达式 | 例如: "div>p"中的 "div", "p" |
| 块表达式类型 | 例如: "div"的类型是TAG, ".red"的类型是CLASS, "div.red"则是 TAG+ CLASS。共有8种块表达式类型:ID,CLASS,NAME, ATTR,TAG, CHILD, POS, PSEUDO |
| 块间关系符 | 表示块表达式之间关系的符号, 例如:"div>p"中的">", 共有4中块间关系符, ">"父子关系, ""祖先后代关系, "+"紧挨着的兄弟元素, "~"之后的所有兄弟元素 |
选择器表达式由块表达式和块间关系符组成。其中, 块表达式分为3种, 简单表达式,属性表达式,伪类表达式, 块间关系符分为4种: ">"父子关系, ""祖先后代关系, "+"紧挨着的兄弟元素, "~"之后的所有兄弟元素。
3, 选择器执行逻辑
# 1, 处理选择器表达式:解析选择器表达式中的块表达式和块间关系符
# 2, 处理块表达式:用块表达式的一部分查找,用剩余部分对查找结果进行过滤
# 3, 处理块间关系符, 按照块间关系符查找,用块表达式对查找结果进行过滤
Sizzle执行的思路 它是一款从右向左查找的选择器引擎,每一步都有核心接口
# 1, 正则chunker负责从选择器表达式中提取块表达式和块间关系符
# 2, 方法Sizzle.find(expr, context, isXML)负责查找块表达式匹配的与元素集合, 方法
Sizzle.filter(expr, set, inplace, not) 负责用块表达式过滤元素集合
# 3, 对象Sizzle.selector.relative中的块间关系过滤函数根据块间关系符过滤元素集合。
4, Sizzle(Selector, context, results, seed)
函数Sizzle(selector, context, results, seed)用于查找与选择器表达式selector匹配的元素集合。该函数式选择器引擎的入口。
函数Sizzle执行的6个关键步骤:
1, 解析块表达式和块间关系符
2, 如果存在位置伪类,则从左向右查找
a: 查找第一个块表达式匹配的元素集合, 得到第一个上下文元素集合
b: 遍历剩余的块表达式和块间关系符,不断缩小上下文元素集合
3,否则从右向左查找
a: 查找最后一个块表达式匹配的元素集合,得到候选集,映射集
b: 遍历剩余的块表达式和块间关系符,对映射集执行块间关系过滤
4,根据映射集筛选候选集,将最终匹配的元素放入结果集
5,如果存在并列选择器表达式, 则递归调用Sizzle(selector, context, results, seed)查找匹配的元素集合, 并合并排序,去重
6, 最后返回结果集
5, 方法总结
1, Sizzle(selector, context, results, seed)
用于查找与选择器表达式selector匹配的元素集合,如果浏览器支持原生方法querySelectorAll(), 则调用该方
法查找元素,如果不支持,则模拟该方法的行为
2,正则chunker
用于从选择器表达式中提取表达式和块间关系符
3,Sizzle.find(expr, context, isXML)
负责查找与块表达式匹配的元素集合, 该方法安排表达式类型数组Sizzle.selectors.order规定的查找顺序
(ID,CLASS, NAME, TAG)挨个尝试查询,如果未找到,则查找上下文的所有后代元素(*)
4,Sizzle.fillter(expr, set, inplace, not)
负责用块表达式过滤元素集合, 该方法通过调用过滤函数集合Sizzle.selectors.filter中的过滤函数来执行过滤
操作
5,Sizzle.selectors.order
定义了查找单个块表达式时的查找顺序,依次是 ID、CLASS、NAME、TAG。其中,CLASS 需要浏览器支持方法
getElementsByClassName()
6,Sizzle.selectors.match/leftMatch
中存放了表达式类型和正则的映射,正则用于确定块表达式的类型,并解析其中的参数.
7,Sizzle.selectors.find
定义了ID、CLASS、NAME、TAG 所 对 应 的 查 找 函 数。 其中,CLASS 需要浏览器支持方法
getElementsByClassName()。查找函数会返回数组或undefined,内部通过调用相应的原生方法来查找元素
8,Sizzle.selectors.relative
存放了块间关系符和对应的块间关系过滤函数。块间关系过滤函数用于检查映射集 checkSet 中的元素是否匹配块间
关系符左侧的块表达式
9,Sizzle.selectors.preFilter
定义了CLASS、ID、TAG、CHILD、ATTR、PSEUDO、POS 所对应的预过滤函数。预过滤函用于在过滤函数之前修正与
过滤操作相关的参数
10,Sizzle.selectors.filters
定义了一组伪类和对应的伪类过滤函数。伪类过滤函数负责检查元素是否匹配伪类,返回一个布尔值
11,Sizzle.selectors.setFilters
中定义了一组位置伪类和对应的伪类过滤函数。位置伪类
过滤函数通过比较下标来确定元素在集合中的位置,返回一个布尔值
12,Sizzle.selectors.filter
定义了 PSEUDO、CHILD、ID、TAG、CLASS、ATTR、POS
对应的过滤函数。过滤函数负责检查元素是否匹配过滤表达式,返回一个布尔值