第二章 jQuery技术解密 (六)

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" 进行判断,把不等于该值的元素从结果集中去掉。

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

不过,对于 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
copy

  1. <scripttype="text/javascript">
  2. /*!
  3. *SizzleCSSSelectorEngine-v0.9.3
  4. *Copyright2009,TheDojoFoundation
  5. *ReleasedundertheMIT,BSD,andGPLLicenses.
  6. *Moreinformation:http://sizzlejs.com/
  7. */
  8. //把Sizzle引擎封装在一个独立的空间中
  9. (function(){
  10. //定义用于块识别器的正则表达式
  11. varchunker=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^>+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,
  12. done=0,
  13. toString=Object.prototype.toString;
  14. //Sizzle选择器引擎构造器函数
  15. //参数说明:
  16. //selector:选择器字符串
  17. //context:上下文
  18. //results:结果集
  19. //seed:种子
  20. varSizzle=function(selector,context,results,seed){
  21. //省略的函数体
  22. };
  23. //Sizzle匹配函数
  24. //参数说明:
  25. //expr:匹配表达式
  26. //set:条件设置选项
  27. Sizzle.matches=function(expr,set){
  28. returnSizzle(expr,null,null,set);
  29. };
  30. //Sizzle查询函数
  31. //参数说明:
  32. //expr:查询表达式
  33. //context:上下文
  34. //isXML:检测函数
  35. Sizzle.find=function(expr,context,isXML){
  36. //省略的函数体
  37. };
  38. //Sizzle过滤函数
  39. //参数说明:
  40. //expr:过滤表达式
  41. //set:条件设置选项
  42. //inplace:包含项
  43. //not:排除项
  44. Sizzle.filter=function(expr,set,inplace,not){
  45. //省略的函数体
  46. };
  47. //Sizzle表达式对象
  48. //列举所用的各种匹配表达式
  49. varExpr=Sizzle.selectors={
  50. //省略成员属性
  51. };
  52. //省略其他辅助性工具函数和逻辑代码暴露的接口
  53. jQuery.find=Sizzle;
  54. jQuery.filter=Sizzle.filter;
  55. jQuery.expr=Sizzle.selectors;
  56. jQuery.expr[":"]=jQuery.expr.filters;
  57. //定义的公共函数
  58. Sizzle.selectors.filters.hidden=function(elem){};
  59. Sizzle.selectors.filters.visible=function(elem){};
  60. Sizzle.selectors.filters.animated=function(elem){};
  61. jQuery.multiFilter=function(expr,elems,not){};
  62. jQuery.dir=function(elem,dir){};
  63. jQuery.nth=function(cur,result,dir,elem){};
  64. jQuery.sibling=function(n,elem){};
  65. return;
  66. window.Sizzle=Sizzle;
  67. })();
  68. </script>

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

时间: 2024-07-28 22:26:55

第二章 jQuery技术解密 (六)的相关文章

第二章 jQuery技术解密(一)

2.2 jQuery 原型技术分解 任何复杂的技术都是从最简单的问题开始的,如果你被 jQuery 几千行庞杂结构的源代码所困惑,那么建议你阅读本节内容,我们将探索 jQuery 是如何从最简单的问题开始,并逐步实现羽翼渐丰的演变过程,从 jQuery 核心技术的还原过程来理解 jQuery 框架的搭建原理. 2.2.1 起源 -- 原型继承 用过 JavaScript 的读者都会明白,在 JavaScript 脚本中到处都是函数,函数可以归置代码段,把相对独立的功能封装在一个函数包中.函数也可

第二章 jQuery技术解密 (二)

2.2.6 延续 -- 迭代器 在 jQuery 框架中,jQuery 对象是一个很奇怪的概念,具有多重身份,所以很多初学者一听说 jQuery 对象就感觉很是不解,误以为它是 John Resig 制造的新概念.我们可以对jQuery 对象进行如下分解. 第一,jQuery 对象是一个数据集合,它不是一个个体对象.因此,你无法直接使用 JavaScript 的方法来操作它. 第二,jQuery 对象实际上就是一个普通的对象,因为它是通过 new 运算符创建的一个新的实例对象.它可以继承原型方法

第二章 jQuery技术解密 (四)

2.3.4 生成 DOM 元素 jQuery.fn.init() 构造函数能够构建 jQuery 对象,并把匹配的 DOM 元素存储在 jQuery 对象内部集合中.jQuery.fn.init() 构造函数可以接收单个的 DOM 元素,也可以接收 DOM 集合.如果接收的是字符串型 ID 值,则直接在文档中查找对应的 DOM 元素,并把它传递给 jQuery 对象:如果接收的是字符串型 HTML 片段,则需要把这个字符串片段生成 DOM 元素.下面我们将重点分析 jQuery 是如何把 HTM

第二章 jQuery技术解密 (五)

2.3.5 引用 DOM 元素 jQuery() 函数能够直接接受 HTML 字符串,并把它们转换为 DOM 结构,这是上一节中所讲解的利用 jQuery() 函数生成 DOM 元素.当然,我们也可以看到 jQuery() 函数还可以接收 DOM 元素.DOM元素集合.HTML标签或者 ID 值.下面我们就来分析 jQuery.fn.init() 构造器是如何把这些类型的参数转换为 DOM 元素的. 对于 HTML 标签来说,它使用 document.getElementsByTagName()

第二章 jQuery技术解密 (七)

2.4.5 Sizzle 构造器 在 jQuery.fn.init() 构造器中,通过调用 jQuery(context).find(selector) 函数来解析并匹配 DOM 元素.jQuery.find() 函数实际上是引用 Sizzle() 函数,而 Sizzle() 函数仅是 Sizzle 引擎的构造器,它主要调用 Sizzle.find() 函数在 DOM 文档树中查找与 CSS 语法相匹配 DOM 的元素节点的集合.jQuery 名字中 Query 的意义就体现在这里.下面我们来分

《众妙之门——JavaScript与jQuery技术精粹》——第1章 初学JavaScript 需知的七件事 1.1 缩略标记

第1章 初学JavaScript 需知的七件事 我很早以前就开始编写JavaScript代码,很高兴看到这种语言在今天所取得的成功,能成为这个成功故事中的一部分我很开心.关于JavaScript,我写过许多文章.章节以及一整本书,直到今天我仍在寻找新的东西.下文是一些我工作学习过程中激动时刻的记录,大家与其守株待兔,不如自己尝试去体会这种感受. 1.1 缩略标记 众妙之门--JavaScript与jQuery技术精粹 在创建对象和数组过程中可以使用缩略标记是我喜欢JavaScript的重要原因之

&amp;gt; 第二章 NGWS Runtime 技术基础(rainbow 翻译) (转自重粒子空

<<展现C#>> 第二章 NGWS Runtime 技术基础(rainbow 翻译)   出处:http://www.informit.com/matter/ser0000001/chapter1/ch02.shtml 正文: 第二章  NGWS  runtime 技术基础     既然你已经具有了C#全面的印象,我也想让你了解NGWS runtime的全貌.C#依靠由NGWS提供的运行时:因此,有必要知道运行时如何工作,以及它背后所蕴含的概念.    所以,这一章分为两部分--它

Programming MS Office 2000 Web Components第二章第二节

web 第二章第二节 电子表格组件的高级功能 我们已经讨论了电子表格组件的大部分基本功能,现在让我们转向一些高级功能.大部分的这些高级功能Excel2000都不包含,因为这些是组件专门需要的特殊功能.而那些Excel2000中存在的功能,在电子表格组件中也被增强,使得可以提供一些新的功能. 属性绑定和实时数据 "属性绑定"是电子表格组件中最新奇的新功能之一,它是指控件能够将同一个web页面上其它对象的属性和方法用作单元值或公式参数的能力.电子表格控件使用标准的COM机制来实现绑定到属性

《众妙之门——JavaScript与jQuery技术精粹》——导读

前 言 众妙之门--JavaScript与jQuery技术精粹 对于网站开发设计人员而言,在面对选择解决方案时做出正确的决定并不容易.不论是在建立复杂的网站应用还是在改进网站的过程中,都会有很多前期解决方案可供选择,有时选择最合适的一款方案至关重要.本书着重讲述了在选择相应解决方案时务必要注意的事项,即是否稳定并易于定制.是否有实用性并易于理解.是否具有可维护性.兼容性,以及功能的可拓展性. 本书重点阐述了检验代码的重要性以及在执行JavaScript程序时需要避免的问题.所选择的解决方案应能符