jQuery技术

jQuery :技术解密(六)

字号+ 作者:H5之家 来源:H5之家 2016-12-14 16:05 我要评论( )

jQuery :技术解密(六) 2.4 解析 jQuery 选择器引擎 SizzlejQuery 从 1.3 版本开始,使用了新的选择器引擎 Sizzle(官方网址 ) 。Sizzle 是 jQuery 作者 John Resig 开发的 DOM 选择器引擎 (Dom Selector Engine),速度号称业界第一。而且它有一个重要的特点

jQuery :技术解密(六)


2.4 解析 jQuery 选择器引擎 SizzlejQuery 从 1.3 版本开始,使用了新的选择器引擎 Sizzle(官方网址 ) 。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" 进行判断,把不等于该值的元素从结果集中去掉。

不仅是这个选择器,对于所有形式的复合选择器,都可以根据这种思路来拆分选择器,然后分别完成每一部分的操作。

不过,对于 jQuery 选择器引擎来说,不同版本在设计思路上也存在一些细微的区别。例如,针对下面的选择器:

$("div p");

早期的 jQuery 选择器是根据下面的步骤进行解析的。

第一步,根据 document.getElementsByTagName() 方法选择文档树中的 div 元素集合。
第二步,迭代 div 元素集合,在所有的 div 元素中查找每个 div 元素下的 p 元素。
第三步,合并结果。

而 Sizzle 选择器适当调整了解析的顺序。

第一步,根据 document.getElementsByTagName() 方法选择文档树中的 p 元素集合。
第二步,迭代 p 元素集合,在所有的 p 元素中查找每个 p 元素的父级元素。
第三步,检测父级元素。如果不是 div 元素,则遍历上一级元素;如果迭代到文档树的顶层,则排除该 p 元素;如果是 div 元素,则保存该 p 元素。

经过比较可以看到,Sizzle 选择器放弃了合并结果操作,直接在遍历过程中过滤元素,所以导致查找速度大幅提升。

初步把握了 jQuery 选择器的基本工作原理,那么我们就可以了解 jQuery 在匹配元素时是如何工作的。例如,对于 $("div[id=value]");选择器,我们知道 JavaScript 先匹配 div 元素,然后再判断 div 元素的 id 属性是否等于 value ,如果不等于就从结果集中删除掉。而对于 $("div#value"); 选择器也是一样,可以看到 div#value 与 div[id=value] 是完全相同的操作。同样, div.class 也可以转换为属性选择符进行操作。

2.4.3 选择器和过滤器根据上面的分析,我们可以把 CSS选择器拆分为两大组成部分,或者说可以把选择器分为以下两类。

第一类,选择器 (selector) 。即根据给定的选择符,从 DOM 文档树找到相关的元素节点,并存储到结果集中。
第二类,过滤器 (filter) 。根据表达式的条件,在结果集中过滤元素。

于是,这里就有一个技术难题:哪些选择符适合选择器(selector),哪些选择符适合过滤器(filter) ?

由于可以直接使用 document.getElementsByTagName() 方法进行选择,因此对于类型选择器来说,直接使用选择即可。

类型选择器相互之间还可以组成各种形式的复合选择器。例如:

*

E F

E~F

E+F

E>F

E/F

E

虽然这些特殊形式的类型选择器由多个选择器构成,但是它们都可以从文档中直接选择,故我们可以统一把它们归为选择器类型。而对于 ID 选择器和 Class 选择器来说:

如果它们在 selector 字符串的起始位置,那么它们也可以完成选择功能。例如 $("#id");、$(".class");。
如果它们不在起始位置,那么就应该作为筛选器实现。例如 $("div#id"); 、$("div.class"); 。

实际上,由于 ID 选择器可以直接调用 JavaScript 的 document.getElementById() 方法进行选择。但是对于 Class 选择器来说,由于它无法直接进行选择,因此,我们可以把ID选择器视为选择器,而把 Class 选择器视为过滤器。

对于 Class 选择器来说,还可以把它转换为 "*.class" 形式。其中的 "*" 表示先选取范围内所有的元素,然后再进行过滤,这样就可以实现统一的编程接口。

对于属性选择器、伪类选择器来说,它们只能够附在其他选择器后面使用,如果单独使用,则可以通过在前面添加 * 标签,以便设计成统一的语法格式,以方便选择器引擎的工作。它们与 Class 选择器的工作方式是相同的,即都视为过滤器。

例如,对于下面这个复杂的选择器来说,包含了类型选择器、Class选择器、ID选择器、属性选择器和伪类选择器。

$("div.red:nth-child(odd)[title=bar]#wrap p");

jQuery 解析的步骤如下。

第一步,选择 DOM 文档树中所有的 p 元素,建立初步结果集。

第二步,在结果集中,选择父级元素为 div 的元素,形成新的结果集。

第三步,在新的结果集中筛选class属性为 red 的元素

第四步,解析伪类选择器 :nth-child(odd),在结果集中筛选元素的子元素为偶数的元素。

第五步,解析属性选择器 [title=bar] ,在结果集中筛选元素的 title 属性为 bar 的元素。

第六步,在结果集中筛选 id 等于 wrap 的元素。

2.4.4 Sizzle 引擎结构jQuery 的CSS 选择器引擎 Sizzle 共有 1000 多行代码,占据了 jQuery 框架四分之一的份额。这些代码被直接调用的匿名函数封装在一个独立的空间中,外界是无法访问的。通过下面代码可以把引擎接口传递给 jQuery 空间下的四个公共函数。

jQuery.find = Sizzle;

jQuery.filter = Sizzle.filter;

jQuery.expr = Sizzle.selectors;

jQuery.expr[":"] = jQuery.expr.filters;

Sizzle 引擎在jQuery 框架中的位置犹如咽喉,起到了核心作用,如图 2.4 所示。在下面的 jQuery 选择器逻辑流程图中,首先,对传入的选择符参数进行过滤,只有是表达式字符串时,才会进入 jQuery.fn.find() 入口,然后进入 Sizzle 接口 (jQuery.find())
,在 Sizzle 构造器中分别调用 Sizzle.find() 和 Sizzle.filter() 函数完成选择和过滤操作。



在选择 (Sizzle.find()) 和过滤 (Sizzle.filter()) 操作过程中,都会经过这样的处理流程:匹配简单的选择器类型 (To match singleselector type) 。

ID 选择器

Name

Class 选择器

类型选择器

........

对于选择器 (Sizzle.find()) 来说,它会调用 Expr.find[type] ,而对于过滤器 (Sizzle.filter()) 来说,它会调用 Expr.filter[type],最后分别返回 Sizzle.find() 和 Sizzle.filter()。

在Sizzle.filter() 过程中,它还需要检测关系选择符是否存在 (To check any relative exisits?) 。如果存在,则调用 Expr.relative[],最后返回结果集。

下面对 Sizzle 引擎的主体结构进行简单的分析。

[html] view
plain

<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:

*/



// 把 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>

通过上面的结构分析,可以看到 Sizzle 引擎主要包括一个构造器 Sizzle(),三个核心功能函数 matches()、find() 和 filter() ,以及一个表达式对象 selectors 。下面分别对它们进行讲解。

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • jQuery :技术解密(三)

    jQuery :技术解密(三)

    2016-12-15 12:01

  • jQuery LigerUI 使用教程入门篇

    jQuery LigerUI 使用教程入门篇

    2016-12-14 15:05

  • [幻灯片代码] jQuery将任何HTML内容转换为水平或垂直的类似旋转木马样式的幻灯片特效代码

    [幻灯片代码] jQuery将任何HTML内容转换为水平或垂直的类似旋转木马

    2016-12-14 14:02

  • GA小技巧:使用jQuery来方便的布置事件跟踪代码

    GA小技巧:使用jQuery来方便的布置事件跟踪代码

    2016-12-13 18:00

网友点评