2.4 解析 jQuery 选择器引擎 Sizzle
jQuery 从 1.3 版本开始,使用了新的选择器引擎 Sizzle(官方网址 http://sizzlejs.com) 。Sizzle 是 jQuery 作者 John Resig 开发的 DOM 选择器引擎 (Dom Selector Engine),速度号称业界第一。而且它有一个重要的特点就是 Sizzle 是完全独立于 jQuery 的,如果用户不想用 jQuery ,还可以只用 Sizzle 。
Sizzle 选择器引擎目前成为 jQuery 框架默认的选择器引擎,相比原来的 jQuery 引擎,速度有很大的提升,如图 2.3 所示的各种选择器执行效率的对比。
2.4.1 回顾CSS的选择器
在解析 jQuery 选择器引擎 Sizzle 之前,我们不妨回顾一下 CSS 的选择器 (CSS selector) 。CSS选择器可以分为三种基本类型:ID选择器 (#id)、Class选择器(.class)和类型(type)选择器(p)。
另外,CSS还支持高级选择器,如属性选择器 (attribute)、伪类或伪对象选择器 (Pseudo Classes) 等。这些都是单一的选择器,可以在应用中把它们组合起来,形成组合选择器,如 div#id,div:last-child。组合型选择器又包括多种关系形式,如包含关系、并列关系、相邻关系和父子关系等。
2.4.2 解析 jQuery 选择器引擎的设计思路
尽管 jQuery 选择器引擎 Sizzle 非常复杂,功能也非常强大,但是它们都是建立在 JavaScript 已定义的方法或属性基础上来实现的。这主要包括元素的 getElementsByTagName() 和 getElementById() 方法,以及元素的 childNodes、firstChild、lastChild、nextSibling、parentNode 和 previousSibling 属性。借助这些方法和属性可以直接或间接地选择相匹配的 DOM 元素。
为了方便讲解,我们结合一个选择器进行说明。选择器代码如下所示。
$("div.red");
这是一个复合选择器,一搬读者可以这样分析:在DOM文档树中找到 class 属性等于 "red" 的 div 元素。即一步到位,直接从DOM文档树中选择所需要的元素。实际上,如果根据选择器引擎的工作方式,可以把这个字符串拆分成两步走。
- 第一步,根据 document.getElementsByTagName() 方法选择文档树中的 div 元素集合。
- 第二步,根据其 class 是否等于 "red" 进行判断,把不等于该值的元素从结果集中去掉。
- 第一步,根据 document.getElementsByTagName() 方法选择文档树中的 div 元素集合。
- 第二步,迭代 div 元素集合,在所有的 div 元素中查找每个 div 元素下的 p 元素。
- 第三步,合并结果。
- 第一步,根据 document.getElementsByTagName() 方法选择文档树中的 p 元素集合。
- 第二步,迭代 p 元素集合,在所有的 p 元素中查找每个 p 元素的父级元素。
- 第三步,检测父级元素。如果不是 div 元素,则遍历上一级元素;如果迭代到文档树的顶层,则排除该 p 元素;如果是 div 元素,则保存该 p 元素。
2.4.3 选择器和过滤器
- 第一类,选择器 (selector) 。即根据给定的选择符,从 DOM 文档树找到相关的元素节点,并存储到结果集中。
- 第二类,过滤器 (filter) 。根据表达式的条件,在结果集中过滤元素。
- 如果它们在 selector 字符串的起始位置,那么它们也可以完成选择功能。例如 $("#id");、$(".class");。
- 如果它们不在起始位置,那么就应该作为筛选器实现。例如 $("div#id"); 、$("div.class"); 。
2.4.4 Sizzle 引擎结构
- <script type="text/javascript">
- /*!
- * Sizzle CSS Selector Engine - v0.9.3
- * Copyright 2009, The Dojo Foundation
- * Released under the MIT, BSD, and GPL Licenses.
- * More information: http://sizzlejs.com/
- */
- // 把 Sizzle 引擎封装在一个独立的空间中
- (function(){
- // 定义用于块识别器的正则表达式
- var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,
- done = 0,
- toString = Object.prototype.toString;
- // Sizzle 选择器引擎构造器函数
- // 参数说明:
- // selector: 选择器字符串
- // context: 上下文
- // results: 结果集
- // seed: 种子
- var Sizzle = function(selector, context, results, seed) {
- // 省略的函数体
- };
- // Sizzle 匹配函数
- // 参数说明:
- // expr: 匹配表达式
- // set: 条件设置选项
- Sizzle.matches = function(expr, set){
- return Sizzle(expr, null, null, set);
- };
- // Sizzle 查询函数
- // 参数说明:
- // expr: 查询表达式
- // context: 上下文
- // isXML: 检测函数
- Sizzle.find = function(expr, context, isXML){
- // 省略的函数体
- };
- // Sizzle 过滤函数
- // 参数说明:
- // expr: 过滤表达式
- // set: 条件设置选项
- // inplace: 包含项
- // not: 排除项
- Sizzle.filter = function(expr, set, inplace, not){
- // 省略的函数体
- };
- // Sizzle 表达式对象
- // 列举所用的各种匹配表达式
- var Expr = Sizzle.selectors = {
- // 省略成员属性
- };
- // 省略其他辅助性工具函数和逻辑代码暴露的接口
- jQuery.find = Sizzle;
- jQuery.filter = Sizzle.filter;
- jQuery.expr = Sizzle.selectors;
- jQuery.expr[":"] = jQuery.expr.filters;
- // 定义的公共函数
- Sizzle.selectors.filters.hidden = function(elem){};
- Sizzle.selectors.filters.visible = function(elem){};
- Sizzle.selectors.filters.animated = function(elem){};
- jQuery.multiFilter = function(expr, elems, not){};
- jQuery.dir = function(elem, dir){};
- jQuery.nth = function(cur, result, dir, elem){};
- jQuery.sibling = function(n, elem){};
- return;
- window.Sizzle = Sizzle;
- })();
- </script>