jQuery技术内幕:深入解析jQuery架构设计与实现原理. 3.9 Sizzle.selectors

3.9 Sizzle.selectors

对象Sizzle.selectors包含了Sizzle在查找和过滤过程中用到的正则、查找函数、过滤函数,其中包含的属性见图3-1,源码结构见代码清单3-1。

3.9.1 Sizzle.selectors.order

表达式类型数组Sizzle.selectors.order中定义了查找单个块表达式时的查找顺序,依次是ID、CLASS、NAME、TAG。其中,CLASS需要浏览器支持方法getElementsByClass

Name()。查找顺序综合考虑了浏览器是否支持、查找结果集的大小、查找效率、使用频率等因素。

相关代码如下所示:

4221 var Expr =
Sizzle.selectors = {

4222    
order: [ "ID", "NAME", "TAG" ],

 

4749 };

 

5139 (function(){

5140    
var div = document.createElement("div");

5141

5142    
div.innerHTML = "<div class='test e'></div><div
class='test'></div>";

5143

5144    
// Opera can't find a second classname (in 9.6)

5145    
// Also, make sure that getElementsByClassName actually exists

5146    
if ( !div.getElementsByClassName ||
div.getElementsByClassName("e").length === 0 ) {

5147        
return;

5148    
}

5149

5150    
// Safari caches class attributes, doesn't catch changes (in 3.2)

5151    
div.lastChild.className = "e";

5152

5153    
if ( div.getElementsByClassName("e").length === 1 ) {

5154        
return;

5155    
}

5156    

5157    
Expr.order.splice(1, 0, "CLASS");

5158    
Expr.find.CLASS = function( match, context, isXML ) {

5159        
if ( typeof context.getElementsByClassName !== "undefined"
&& !isXML ) {

5160             return
context.getElementsByClassName(match[1]);

5161        
}

5162  
  };

5163

5164    
// release memory in IE

5165    
div = null;

5166 })();

第5140~5155行:测试当前浏览器是否正确支持方法getElementsByClassName()。测试思路是先构造一段DOM结构,然后调用方法getElementsByClassName(),检查是否返回期望数量的元素。如果不支持或不能正确支持,则不做任何事情。

第5157~5162行:如果当前浏览器支持方法getElementsByClassName(),则:

向Sizzle.selectors.order中插入"CLASS",由["ID", "NAME", "TAG"]变为["ID",
"CLASS", "NAME", "TAG"],插入位置在"ID"之后、"NAME"之前。

向Sizzle.selectors.find中插入"CLASS"对应的查找函数。

3.9.2 Sizzle.selectors.match/leftMatch

对象Sizzle.selectors.match/leftMatch中存放了表达式类型和正则的映射,正则用于确定块表达式的类型,并解析其中的参数。

相关代码如下所示:

4221 var Expr = Sizzle.selectors = {

 

4224    
match: {

4225        
ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,

4226        
CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,

4227        
NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,

4228        
ATTR:
/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,

4229        
TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,

4230        
CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|

(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,

4231        
POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,

4232        
PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]

*)+)\2\))?/

4233    
},

 

4749 };

 

4751 var origPOS = Expr.match.POS,

4752    
fescape = function(all, num){

4753        
return "\\" + (num - 0 + 1);

4754    
};

4755

4756 for ( var type in Expr.match ) {

4757  
  Expr.match[ type ] = new RegExp(
Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );

4758    
Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source +
Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );

4759 }

第4224~4233行:定义一组正则,稍后会逐个分析和测试。

第4756~4759行:为对象Sizzle.sectors.match中的正则增加一段后缀正则/(?![^\[]*\])(?![^\()*\])/,然后再加上一段前缀正则/(^(?:.|\r|\n)*?)/,来构造对象Sizzle.sectors.leftMatch中的同名正则。因为增加的前缀正则中包含了一个分组,所以原正则中的分组编号需要加1后移。

1.?后缀正则/(?![^\[]*\])(?![^\()*\])/

后缀正则/(?![^\[]*\])(?![^\()*\])/要求接下来的字符不能含有"]"、")",用于确保选择器表达式的语法正确,以及确保正确匹配嵌套选择器表达式。

例如,执行$("input[name=foo\\.baz]]")时会抛出语法异常,因为选择器表达式"input

[name=foo\\.baz]]"的末尾多了一个"]",对象Sizzle.selectors.match/leftMatch中没有正则可以匹配其中的"[name=foo\\.baz]]";如果没有后缀正则,则Sizzle.selectors.match/leftMatch.NAME会匹配"[name=foo\\.baz]]",并执行查找,而不会抛出语法异常。

又如,执行$("input[name=foo.baz]")时会抛出语法异常,因为选择器表达式"input

[name=foo.baz]"没有转义点号,对象Sizzle.selectors.match/leftMatch中没有正则可以匹配其中的"[name=foo.baz]";如果没有后缀正则,则对象Sizzle.selectors.match/leftMatch.CLASS会匹配"input[name=foo.baz]"中的".baz",并执行查找,然后用"input[name=foo
]"过滤查找结果,而不会抛出语法异常。

再如,执行$("input:not(.foo)")时,会先查找匹配"input"的元素集合,然后从中过滤不匹配".foo"的元素集合;如果没有后缀正则,则会变为先查找匹配".foo"的元素集合,然后从中过滤匹配"input:not()"的元素集合。

读者可以将第4757行代码注释掉,然后测试和验证上述例子。

2.?前缀正则/(^(?:.|\r|\n)*?)/

前缀正则/(^(?:.|\r|\n)*?)/用于捕获匹配正则的表达式之前的字符,主要是捕获转义反斜杠,以支持将特殊字符作为普通字符使用。

例如,".test\\#id",在用正则Sizzle.selectors.match.ID匹配时会发现"#"之前是转义反斜杠"\\",这时将认为该表达式的类型不是ID;如果没有前缀正则,则会先查找匹配"#id"的元素集合,然后从中过滤出匹配".test\\"的元素集合。

接下来分析和测试对象Sizzle.selectors.match中ID、CLASS、NAME、ATTR、TAG、CHILD、POS、PSEUDO对应的正则。测试用例参考自Sizzle的测试用例https://github.com/jquery/sizzle/blob/1.7.1/test/unit/selector.js。

3.?ID

正则Sizzle.selector.match.ID用于匹配简单表达式"#id",并解析"#"之后的字符串,其中含有1个分组:id。解析图见图3-7,测试用例见表3-3。

 

图3-7 正则Sizzle.selectors.match.ID

表3-3 正则 Sizzle.selectors.match.ID

序  号         测 试 用 例  运行结果

1       ID.exec("#id") ["#id", "id"]

2       ID.exec("#firstp#simon1")       ["#firstp", "firstp"]

3       ID.exec("#台北Ta?ibe?i")       ["#台北Ta?ibe?i",
"台北Ta?ibe?i"]

4       ID.exec("#foo\\:bar")      ["#foo\\:bar",
"foo\\:bar"]

5       ID.exec("#test\\.foo\\[5\\]bar")      ["#test\\.foo\\[5\\]bar",
"test\\.foo\\[5\\]bar"]

 

4.?CLASS

正则 Sizzle.selector.match.CLASS 用于匹配简单表达式".class",并解析"."之后的字符串,其中含有1个分组:类样式。解析图见图3-8,测试用例见表3-4。

 

图3-8 正则Sizzle.selectors.match.CLASS

表3-4  正则
Sizzle.selectors.match.CLASS

序  号         测 试 用 例  运
行 结 果

1       CLASS.exec(".blog")         [".blog", "blog"]

2       CLASS.exec(".blog.link")  [".blog", "blog"]

3       CLASS.exec(".台北Ta?ibe?i") [".台北Ta?ibe?i",
"台北Ta?ibe?i"]

4       CLASS.exec(".foo\\:bar")          [".foo\\:bar",
"foo\\:bar"]

5       CLASS.exec(".test\\.foo\\[5\\]bar")         [".test\\.foo\\[5\\]bar",
"test\\.foo\\[5\\]bar"]

 

5.?NAME

正则Sizzle.selector.match.NAME用于匹配属性表达式"[ name
= "value" ]",并解析属性name的值,其中含有1个分组:属性name的值。解析图见图3-9,测试用例见表3-5。

 

图3-9 正则Sizzle.selectors.match.NAME

表3-5 正则 Sizzle.selectors.match.NAME

序  号         测 试 用 例  运
行 结 果

1       NAME.exec("input[name=action]")         ["[name=action]", "action"]

2       NAME.exec("input[name='action']")       ["[name='action']",
"action"]

3       NAME.exec("input[name=\"action\"]")  ["[name="action"]",
"action"]

4       NAME.exec("input[name=\"types[]\"]")          null

 

6.?ATTR

正则Sizzle.selector.match.ATTR用于匹配属性表达式"[attribute
= "value"]",并解析属性名和属性值,其中含有5个分组:属性名、等号部分、引号、属性值、无引号时的属性值。解析图见图3-10,测试用例见表3-6。

7.TAG

正则Sizzle.selector.match.TAG用于匹配简单表达式"tag",并解析标签名,其中含有1个分组:标签名。解析图见图3-11,测试用例见表3-7。

8.?CHILD

正则Sizzle.selector.match.CHILD用于匹配子元素伪类表达式:nth-child(index/even/odd/equation)、:first-child、:last-child、:only-child,并解析子元素伪类和伪类参数,其中含有2个分组:子元素伪类、伪类参数。解析图见图3-12,测试用例见表3-8。

9.?POS

正则Sizzle.selector.match.POS用于匹配位置伪类表达式":eq(index)"、":gt(index)"、":lt(index)"、":first"、":last"、":odd"、":even",并解析位置伪类和伪类参数,其中含有2个分组:位置伪类、伪类参数。解析图见图3-13,测试用例见表3-9。

10.?PSEUDO

正则 Sizzle.selector.match.PSEUDO 用于匹配伪类表达式,请解析
":" 之后的伪类和伪类参数,其中含有 3 个分组:伪类、引号、伪类参数。解析图见图3-14,测试用例见表3-10。

 

 

表3-6 正则Sizzle.selectors.match.ATTR

序号         测 试 用 例  运
行 结 果

1       ATTR.exec("a[title]")        ["[title]", "title",
undefined, undefined, undefined, undefined]

2       ATTR.exec("a[title=]")     ["[title=]", "title",
"=", undefined, undefined, ""]

3       ATTR.exec("a[rel='bookmark']")      ["[rel='bookmark']",
"rel", "=", "'", "bookmark", undefined]

4       ATTR.exec("a[rel=\"bookmark\"]") ["[rel="bookmark"]",
"rel", "=", """, "bookmark",
undefined]

5       ATTR.exec("a[rel=bookmark]")        ["[rel=bookmark]",
"rel", "=", undefined, undefined, "bookmark"]

6       ATTR.exec("a[rel='bookmark']")      ["[rel='bookmark']",
"rel", "=", "'", "bookmark", undefined]

7       ATTR.exec("input[name=foo\\.baz]")      ["[name=foo\\.baz]",
"name", "=", undefined, undefined, "foo\\.baz"]

8       ATTR.exec("input[name=foo\\[baz\\]]") ["[name=foo\\[baz\\]]",
"name", "=", undefined, undefined, "foo\\

[baz\\]"]

9       ATTR.exec("a[href='http://www.google.com/']")   ["[href='http://www.google.com/']",
"href", "=", "'",
"http://www.google.com/", undefined]

10     ATTR.exec("a[href^='http://www']")      ["[href^='http://www']",
"href", "^=", "'", "http://www",
undefined]

11     ATTR.exec("a[href$='org/']")  ["[href$='org/']", "href",
"$=", "'", "org/", undefined]

12     ATTR.exec("a[href*='google']")       ["[href*='google']",
"href", "*=", "'", "google", undefined]

13     ATTR.exec("option[value='']") ["[value='']", "value",
"=", "'", "", undefined]

14     ATTR.exec("option[value!='']")        ["[value!='']",
"value", "!=", "'", "", undefined]

15     ATTR.exec("[xml\\:test]")        ["[xml\\:test]",
"xml\\:test", undefined, undefined, undefined, undefined]

16     ATTR.exec("[data-foo]")  ["[data-foo]", "data-foo",
undefined, undefined, undefined, undefined]

 

 

图3-11 正则Sizzle.selectors.match.TAG

表3-7 正则 Sizzle.selectors.match.TAG

序  号         测 试 用 例  运
行 结 果

1       TAG.exec("body")    ["body", "body"]

2       TAG.exec("html")    ["html", "html"]

3       TAG.exec("h1")        ["h1", "h1"]

 

 

表3-8  正则
Sizzle.selectors.match.CHILD

序  号         测 试 用 例  运
行 结 果

1       CHILD.exec("p:first-child")      [":first-child",
"first", undefined]

2       CHILD.exec("p:only-child")      [":only-child",
"only", undefined]

3       CHILD.exec("option:nth-child")       [":nth-child", "nth",
undefined]

4       CHILD.exec("option:nth-child(even)")     [":nth-child(even)",
"nth", "even"]

5       CHILD.exec("option:nth-child(odd)")       [":nth-child(odd)",
"nth", "odd"]

6       CHILD.exec("option:nth-child(1)")  [":nth-child(1)", "nth",
"1"]

7       CHILD.exec("option:nth-child(+1)")         [":nth-child(+1)",
"nth", "+1"]

8       CHILD.exec("option:nth-child(-1)") [":nth-child(-1)", "nth",
"-1"]

9       CHILD.exec("option:nth-child(0n+3)")    [":nth-child(0n+3)",
"nth", "0n+3"]

10     CHILD.exec("option:nth-child(1n)")         [":nth-child(1n)",
"nth", "1n"]

11     CHILD.exec("option:nth-child(n)")  [":nth-child(n)", "nth",
"n"]

12     CHILD.exec("option:nth-child(+n)")         [":nth-child(+n)",
"nth", "+n"]

13     CHILD.exec("option:nth-child(-1n
+ 3)") [":nth-child-1n +
3)", "nth", "-1n + 3"]

14     CHILD.exec("option:nth-child(-n+3)")     [":nth-child(-n+3)",
"nth", "-n+3"]

15     CHILD.exec("option:nth-child(-1n+3)")   [":nth-child(-1n+3)",
"nth", "-1n+3"]

16     CHILD.exec("option:nth-child(2n)")         [":nth-child(2n)",
"nth", "2n"]

17     CHILD.exec("option:nth-child(2n
+ 1)")  [":nth-child(2n+1)",
"nth", "2n+1"]

18     CHILD.exec("option:nth-child(2n
+ 1)")  [":nth-child(2n +
1)", "nth", "2n + 1"]

19     CHILD.exec("option:nth-child(+2n+1)")  [":nth-child(+2n + 1)",
"nth", "+2n + 1"]

20     CHILD.exec("option:nth-child(3n)")         [":nth-child(3n)",
"nth", "3n"]

21     CHILD.exec("option:nth-child(3n+0)")    [":nth-child(3n+0)",
"nth", "3n+0"]

22     CHILD.exec("option:nth-child(3n+1)")    [":nth-child(3n+1)",
"nth", "3n+1"]

23     CHILD.exec("option:nth-child(3n-0)")     [":nth-child(3n-0)",
"nth", "3n-0"]

24     CHILD.exec("option:nth-child(3n-1)")     [":nth-child(3n-1)",
"nth", "3n-1"]

 

 

图3-13 正则Sizzle.selectors.match.POS

表3-9 正则 Sizzle.selectors.match.POS

序  号         测 试 用 例  运
行 结 果

1       POS.exec("p:nth(1)")        [":nth(1)", "nth",
"1"]

2       POS.exec("p:eq(2)")         [":eq(2)", "eq",
"2"]

3       POS.exec("p:gt(3)") [":gt(3)", "gt",
"3"]

4       POS.exec("p:lt(4)")  [":lt(4)", "lt",
"4"]

5       POS.exec("p:first")  [":first", "first",
undefined]

6       POS.exec("p:last")   [":last", "last",
undefined]

7       POS.exec("p:even") [":even", "even",
undefined]

8       POS.exec("p:odd")   [":odd", "odd",
undefined]

 

 

图3-14 正则 Sizzle.selectors.match.PSEUDO

表3-10  正则 Sizzle.selectors.match.PSEUDO

序  号         测 试 用 例  运
行 结 果

1       PSEUDO.exec("p:has(a)")        [":has(a)", "has",
"", "a"]

2       PSEUDO.exec("a:contains(Google)")       [":contains(Google)",
"contains", "", "Google"]

3       PSEUDO.exec("input:focus")   [":focus", "focus",
undefined, undefined]

4       PSEUDO.exec(":input")   [":input", "input",
undefined, undefined]

5       PSEUDO.exec(":radio")   [":radio", "radio",
undefined, undefined]

6       PSEUDO.exec(":checkbox")     [":checkbox",
"checkbox", undefined, undefined]

7       PSEUDO.exec(":text")     [":text", "text",
undefined, undefined]

8       PSEUDO.exec(":radio:checked")     [":radio", "radio",
undefined, undefined]

9       PSEUDO.exec(":checkbox:checked")       [":checkbox",
"checkbox", undefined, undefined]

10     PSEUDO.exec("option:selected")    [":selected",
"selected", undefined, undefined]

11     PSEUDO.exec(":header")         [":header",
"header", undefined, undefined]

12     PSEUDO.exec(":empty") [":empty", "empty",
undefined, undefined]

13     PSEUDO.exec(":parent")          [":parent",
"parent", undefined, undefined]

14     PSEUDO.exec(":hidden")          [":hidden",
"hidden", undefined, undefined]

15     PSEUDO.exec(":visible") [":visible", "visible",
undefined, undefined]

 

3.9.3 Sizzle.selectors.find

对象Sizzle.selectors.find 中定义了ID、CLASS、NAME、TAG所对应的查找函数,称为“查找函数集”。其中,CLASS需要浏览器支持方法getElementsByClassName()。

查找函数会返回元素集合或 undefined,内部通过调用相应的原生方法来查找元素,如表3-11所示。查找函数调用原生方法前会检查上下文是否支持原生方法。

表3-11  查找函数集
Sizzle.selectors.find

序  号         类型         原 生 方 法  说  明

1       ID     getElementById()    查找拥有指定id的第一个元素

2       CLASS       getElementsByClassName()   查获拥有指定类样式的元素集合

3       NAME       getElementsByName()    查获拥有指定name的元素集合

4       TAG  getElementsByTagName()       查找拥有指定标签名的元素集合

 

1.?ID

相关代码如下所示:

4221 var Expr =
Sizzle.selectors = {

 

4340    
find: {

4341        
ID: function( match, context, isXML ) {

4342             if ( typeof context.getElementById
!== "undefined" && !isXML ) {

4343                 var m =
context.getElementById(match[1]);

4344                 // Check parentNode to catch
when Blackberry 4.6 returns

4345                 // nodes that are no longer in
the document #6963

4346                 return m && m.parentNode
? [m] : [];

4347             }

4348        
},

 

4370    
},

 

4749 };

2.?CLASS

相关代码如下所示:

5139 (function(){

        
// 测试浏览器是否支持方法 getElementsByClassName()

        
// 如果不支持,则不做任何事情

 

        
// 如果当前浏览器支持方法 getElementsByClassName()

5157    
Expr.order.splice(1, 0, "CLASS");

5158    
Expr.find.CLASS = function( match, context, isXML ) {

5159        
if ( typeof context.getElementsByClassName !== "undefined"
&& !isXML ) {

5160             return
context.getElementsByClassName(match[1]);

5161        
}

5162    
};

 

5166 })();

3.?NAME

相关代码如下所示:

4221 var Expr =
Sizzle.selectors = {

 

4340    
find: {

 

4350        
NAME: function( match, context ) {

4351             if ( typeof
context.getElementsByName !== "undefined" ) {

4352                 var ret = [],

4353                     results =
context.getElementsByName( match[1] );

4354

4355                 for ( var i = 0, l =
results.length; i < l; i++ ) {

4356                     if (
results[i].getAttribute("name") === match[1] ) {

4357  
                      ret.push(
results[i] );

4358                     }

4359                 }

4360

4361                 return ret.length === 0 ? null
: ret;

4362             }

4363        
},

4364

 

4370    
},

 

4749 };

4.?TAG

相关代码如下所示:

4221 var Expr = Sizzle.selectors = {

 

4340    
find: {

 

4365        
TAG: function( match, context ) {

4366             if ( typeof
context.getElementsByTagName !== "undefined" ) {

4367                 return
context.getElementsByTagName( match[1] );

4368             }

4369        
}

 

4370    
},

 

4749 };

3.9.4 Sizzle.selectors.preFilter

对象Sizzle.selectors.preFilter中定义了类型CLASS、ID、TAG、CHILD、ATTR、PSEUDO、POS所对应的预过滤函数,称为“预过滤函数集”。

在方法Sizzle.filter( expr, set, inplace, not )中,预过滤函数在过滤函数Sizzle.selectors.filter[
type ]之前被调用,见图3-6。调用预过滤函数时的参数格式为:

Sizzle.selectors.preFilter[ type ]( 正则匹配结果 match,
元素集合 curLoop, 是否缩小元素集合 inplace, 新集合 result, 是否取反 not, isXML )

预过滤函数用于在过滤函数之前修正与过滤操作相关的参数,每种类型的预过滤函数其修正行为如表3-12所示。

表3-12  预过滤函数集
Sizzle.selectors.preFilter

序 号     类 型     修 正 行 为  序 号     类 型     修 正 行 为

1       CLASS       过滤不匹配元素,或缩小元素集合     5       ATTR         修正属性名和属性值

2       ID     过滤转义反斜杠     6       PSEUDO   处理:not( selector )的伪类参数

3       TAG  过滤转义反斜杠,转为小写         7       POS  修正位置伪类的参数下标

4       CHILD       格式化子元素伪类参数                           

 

预过滤函数有3种返回值,对应的含义如表3-13所示。

表3-13  预过滤函数的返回值

序  号         返 回 值     说  明

1       false          已经执行过滤,或已经缩小候选集,不需要再执行过滤函数,例如,CLASS

2       true 需要继续执行其他的预过滤函数,尚不到执行过滤函数的时候,例如,在PSEUDO预过滤函数中遇到POS、CHILD时

3       其他         可以调用对应的过滤函数

 

对象Sizzle.selectors.preFilter的总体源码结构如下所示:

var Expr = Sizzle.selectors = {

  
preFilter: {

     
CLASS: function( match, curLoop, inplace, result, not, isXML )  { ... },

     
ID: function( match )  { ... },

     
TAG: function( match, curLoop )  {
... },

     
CHILD: function( match )  { ... },

     
ATTR: function( match, curLoop, inplace, result, not, isXML )  { ... },

     
PSEUDO: function( match, curLoop, inplace, result, not )  { ... },

     
POS: function( match )  { ... }

   },

};

下面对其中的预过滤函数逐个进行介绍和分析。

1.?CLASS

类样式预过滤函数Sizzle.selectors.preFilter.CLASS( match, curLoop, inplace, result,
not, isXML )负责检查元素集合中的每个元素是否含有指定的类样式。如果参数inplace为true,则将不匹配的元素替换为false;如果参数inplace不是true,则将匹配元素放入元素集合result中,以此来不断地缩小元素集合。关于正则Sizzle.selectors.match/leftMatch.CLASS的说明请参见3.9.2节。

相关代码如下所示:

3866    
rBackslash = /\\/g,

 

4221 var Expr =
Sizzle.selectors = {

4371    
preFilter: {

4372        
CLASS: function( match, curLoop, inplace, result, not, isXML ) {

4373             match = " " +
match[1].replace( rBackslash, "" ) + " ";

4374

4375             if ( isXML ) {

4376                 return match;

4377             }

4378

4379  
          for ( var i = 0, elem;
(elem = curLoop[i]) != null; i++ ) {

4380                 if ( elem ) {

4381                     if ( not ^ (elem.className
&& (" " + elem.className +

" "). replace(/[\t\n\r]/g, "
").indexOf(match) >= 0) ) {

4382                         if ( !inplace ) {

4383                             result.push( elem
);

4384                         }

4385

4386                     } else if ( inplace ) {

4387                         curLoop[i] = false;

4388                     }

4389  
              }

4390             }

4391

4392             return false;

4393        
},

 

4475    
},

 

4749 };

第4373行:检查类样式的技巧是在前后加空格,然后用字符串方法indexOf()进行判断。

第4379~4390行:遍历元素集合curLoop,检测每个元素是否含有指定名称的类样式;如果参数not不是true,则保留匹配元素,并排除不匹配元素;如果参数not是true,则保留不匹配元素,排除匹配元素。如果参数inplace为true,则将不匹配的元素替换为false;如果参数inplace不是true,则不修改元素集合curLoop,而是将匹配元素放入元素集合result中,以此来不断地缩小元素集合。

第4381~4388行:这段if-else-if语句块的逻辑有些绕,可以这样理解:if代码块表示的是过滤时通过了的情况,else-if语句块表示的是过滤时未通过的情况。

第4392行:CLASS预过滤函数总是返回false,表示已经执行过滤,或已缩小候选集,不需要再执行CLASS过滤函数。

2.?ID

ID预过滤函数Sizzle.selectors.preFilter.ID(
match )负责过滤转义反斜杠,从匹配结果match中提取并返回id值。关于正则Sizzle.selectors.match/leftMatch.ID的具体说明请参见3.9.2节。

相关代码如下所示:

3866    
rBackslash = /\\/g,

 

4221 var Expr = Sizzle.selectors = {

 

4371    
preFilter: {

 

4395        
ID: function( match ) {

4396             return match[1].replace(
rBackslash, "" );

4397        
},

 

4475    
},

 

4749 };

3.?TAG

标签预过滤函数Sizzle.selectors.preFilter.TAG( match, curLoop )负责过滤转义反斜杠,转换为小写,从匹配结果match中提取并返回标签名。关于正则Sizzle.selectors.match/left

Match.TAG的具体说明参见3.9.2节。

相关代码如下所示:

3866    
rBackslash = /\\/g,

 

4221 var Expr = Sizzle.selectors = {

 

4371    
preFilter: {

 

4399        
TAG: function( match, curLoop ) {

4400             return match[1].replace(
rBackslash, "" ).toLowerCase();

4401        
},

 

4475    
},

 

4749 };

4.?CHILD

子元素伪类预过滤函数Sizzle.selectors.preFilter.CHILD( match )负责将伪类:nth-child

( index/even/odd/equation )的参数格式化为first*n
+ last,例如,将odd格式化为2n+1。关于正则Sizzle.selectors.match/leftMatch.CHILD的具体说明请参见3.9.2节。

相关代码如下所示:

4221 var Expr = Sizzle.selectors = {

 

4371    
preFilter: {

 

4403        
CHILD: function( match ) {

4404             if ( match[1] === "nth"
) {

4405                 if ( !match[2] ) {

4406                     Sizzle.error( match[0] );

4407                 }

4408

4409                 match[2] =
match[2].replace(/^\+|\s*/g, '');

4410

4411                 // parse equations like
'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'

4412                 var test =
/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(

4413                     match[2] ===
"even" && "2n" ||

                         match[2] ===
"odd" && "2n+1" ||

4414                     !/\D/.test( match[2] )
&& "0n+" + match[2] ||

                         match[2]);

4415

4416                 // calculate the numbers
(first)n+(last) including if they are negative

4417                 match[2] = (test[1] + (test[2]
|| 1)) - 0;

4418                 match[3] = test[3] - 0;

4419             }

4420             else if ( match[2] ) {

4421                 Sizzle.error( match[0] );

4422             }

4423

4424             // TODO: Move to normal caching
system

4425    
        match[0] = done++;

4426

4427             return match;

4428        
},

 

4475    
},

 

4749 };

第4409行:替换伪类开头的加号和包含的空格,例如,:nth-child(+1)→:nth-child(1)、

:nth-child(2n + 1)→:nth-child(2n+1)。

第4412~4414行:将伪类参数统一格式化为first*n + last,例如,even→2n、odd→

2n+1、数字→0n+数字。正则/(-?)(\d*)(?:n([+\-]?\d*))?/含有3个分组:负号、first部分、last部分。

第4417~4418行:计算first部分和last部分。注意减0是为了将字符串强制转换为数值。

第4425行:为本次过滤分配一个唯一的标识,用于优化过滤过程,请参见3.9.7节对子元素伪类过滤函数Sizzle.selectors.filter.CHILD( elem, match )的介绍和分析。

5.?ATTR

属性预过滤函数Sizzle.selectors.preFilter.ATTR( match, curLoop, inplace, result,
not, isXML )负责修正匹配结果match中的属性名和属性值。关于正则Sizzle.selectors.match/leftMatch.ATTR的具体说明请参见3.9.2节。

相关代码如下所示:

4221 var Expr =
Sizzle.selectors = {

 

4371    
preFilter: {

 

4430        
ATTR: function( match, curLoop, inplace, result, not, isXML ) {

4431             var name = match[1] =
match[1].replace( rBackslash, "" );

4432            

4433             if ( !isXML &&
Expr.attrMap[name] ) {

4434                 match[1] = Expr.attrMap[name];

4435             }

4436

4437             // Handle if an un-quoted value
was used

4438             match[4] = ( match[4] || match[5]
|| "" ).replace( rBackslash, "" );

4439

4440             if ( match[2] === "~=" )
{

4441                 match[4] = " " + match[4] +
" ";

4442             }

4443

4444             return match;

4445        
},

 

4475    
},

 

4749 };

第4431~4435行:修正属性名。删除转义反斜杠,修正某些特殊属性名。

第4438~4442行:修正属性值。合并分组4和分组5的值,删除转义反斜杠。当属性表达式的属性值有引号时,属性值存储在match[4],否则存储在match[5]。如果等号部分是~=,表示是单词匹配,则在属性值前后加空格;在过滤函数中,对于~=,会在元素的属性值前后加空格,然后用字符串方法indexOf()检查。

6.?PSEUDO

伪类预过滤函数Sizzle.selectors.preFilter.PSEUDO( match, curLoop, inplace, result,
not )主要负责处理伪类表达式是:not( selector )的情况,该函数会将匹配结果match中的分组3(即伪类参数selector)替换为与之匹配的元素集合;对于位置伪类和子元素伪类,则返回true,继续执行各自对应的预过滤函数。关于正则Sizzle.selectors.match/leftMatch.PSEUDO的具体说明请参见3.9.2节。

相关代码如下所示:

4221 var Expr =
Sizzle.selectors = {

 

4371    
preFilter: {

 

4447        
PSEUDO: function( match, curLoop, inplace, result, not ) {

4448             if ( match[1] === "not"
) {

4449                 // If we're dealing with a
complex expression, or a simple one

4450                 if ( ( chunker.exec(match[3])
|| "" ).length > 1 || /^\w/.test(match[3]) ) {

4451                     match[3] =
Sizzle(match[3], null, null, curLoop);

4452

4453                 } else {

4454                     var ret =
Sizzle.filter(match[3], curLoop, inplace, true ^ not);

4455

4456                     if ( !inplace ) {

4457                         result.push.apply(
result, ret );

4458                     }

4459

4460                     return false;

4461                 }

4462

4463             } else if ( Expr.match.POS.test(
match[0] ) || Expr.match.CHILD.test( match[0] ) ) {

4464                 return true;

4465             }

4466            

4467             return match;

4468        
},

 

4475    
},

 

4749 };

第4447行:参数match是正则Sizzle.selectors.match.PSEUDO匹配块表达式的结果,含有3个分组:伪类、引号、伪类参数。

第4448~4461行:如果伪类是:not(selector),则将匹配结果match中的分组3(即伪类参数selector)替换为与之其匹配的元素集合。在对应的过滤函数中,会筛选出不在分组3中的元素。

第4463~4465行:如果是位置伪类POS或子元素伪类CHILD,则返回true,表示仍然需要继续执行各自所对应的预过滤函数。注意,位置伪类POS和子元素伪类CHILD有着自己的预过滤函数。

7.?POS

位置伪类预过滤函数Sizzle.selectors.preFilter.POS( match )负责在匹配结果match的头部插入一个新元素true,使得匹配结果match中位置伪类参数的下标变为了3,从而与伪类的匹配结果保持一致。关于正则Sizzle.selectors.match/leftMatch.POS/PSEUDO的具体说明请参见3.9.2节。

相关代码如下所示:

4221 var Expr = Sizzle.selectors = {

 

4371    
preFilter: {

 

4470        
POS: function( match ) {

4471             match.unshift( true );

4472

4473             return match;

4474        
}

4475    
},

 

4749 };

3.9.5 Sizzle.selectors.filters

对象Sizzle.selectors.filters中定义了一组伪类和对应的伪类过滤函数,称为“伪类过滤函数集”。支持的伪类有::enabled、:disabled、:checked、:selected、:parent、:empty、:has、:header、:text、:radio、:checkbox、:file、:password、:submit、:image、:reset、:button、:input、:focus。方法调用链为:Sizzle.filter()→Sizzle.selectors.filter.PSEUDO()→Sizzle.selectors.filters,如图3-1所示。

伪类过滤函数负责检查元素是否匹配伪类,返回一个布尔值,其参数格式为:

Sizzle.selectors.filters[ 伪类 ]( 元素, 序号, 正则匹配结果, 元素集合 );

// 正则匹配结果是正则
Sizzle.selectors.match.PSEUDO 匹配块选择器表达式的结果,含有 3 个分组:伪类、引号、伪类参数

相关代码如下所示,为了方便解释,代码中增加了示例和注释:

4221 var Expr =
Sizzle.selectors = {

 

4477    
filters: {

            
// $(':enabled') 匹配所有可用元素(未禁用的,不隐藏的)

4478        
enabled: function( elem ) {

4479             return elem.disabled === false
&& elem.type !== "hidden";

4480        
},

4481        
// $(':disabled') 匹配所有不可用元素(禁用的)

4482        
disabled: function( elem ) {

4483             return elem.disabled === true;

4484        
},

4485        
// $(':checked') 匹配所有选中的被选中元素,包括复选框、单选按钮,不包括 option 元素

4486        
checked: function( elem ) {

4487             return elem.checked === true;

4488        
},

4489        
// $(':selected') 匹配所有选中的 option 元素

4490  
      selected: function( elem ) {

4491             // Accessing this property makes
selected-by-default

4492             // options in Safari work properly

4493             if ( elem.parentNode ) {

4494                 elem.parentNode.selectedIndex;

4495             }

4496            

4497             return elem.selected === true;

4498        
},

4499        
// $(':parent') 匹配所有含有子元素或文本的元素

4500        
parent: function( elem ) {

4501             return !!elem.firstChild;

4502        
},

4503        
// $(':empty') 匹配所有不包含子元素或者文本的空元素

4504        
empty: function( elem ) {

4505             return !elem.firstChild;

4506        
},

4507        
// $(':has(selector)') 匹配含有选择器所匹配元素的元素

4508        
has: function( elem, i, match ) {

4509             return !!Sizzle( match[3], elem
).length;

4510        
},

4511        
// $(':header') 匹配如 h1、h2、h3 之类的标题元素

4512        
header: function( elem ) {

4513             return (/h\d/i).test(
elem.nodeName );

4514        
},

4515        
// $(':text') 匹配所有单行文本框

4516        
text: function( elem ) {

4517             var attr = elem.getAttribute(
"type" ), type = elem.type;

4518             // IE6 and 7 will map elem.type to
'text' for new HTML5 types (search, etc)

4519             // use getAttribute instead to
test this case

4520             return elem.nodeName.toLowerCase()
=== "input" && "text" === type && ( attr
=== type || attr === null );

4521        
},

4522        
// $(':radio') 匹配所有单选按钮

4523        
radio: function( elem ) {

4524     
       return
elem.nodeName.toLowerCase() === "input" && "radio"
=== elem.type;

4525        
},

4526        
// $(':checkbox') 匹配所有复选框

4527        
checkbox: function( elem ) {

4528             return elem.nodeName.toLowerCase()
=== "input" && "checkbox" === elem.type;

4529        
},

4530        
// $(':file') 匹配所有文件域

4531        
file: function( elem ) {

4532             return elem.nodeName.toLowerCase()
=== "input" && "file" === elem.type;

4533        
},

4534        
// $(':password') 匹配所有密码框

4535        
password: function( elem ) {

4536             return elem.nodeName.toLowerCase()
=== "input" && "password" === elem.type;

4537        
},

4538        
// $(':submit') 匹配所有提交按钮

4539        
submit: function( elem ) {

4540             var name =
elem.nodeName.toLowerCase();

4541             return (name === "input"
|| name === "button") && "submit" === elem.type;

4542        
},

4543        
// $(':image') 匹配所有图像域

4544        
image: function( elem ) {

4545             return elem.nodeName.toLowerCase()
=== "input" && "image" === elem.type;

4546        
},

4547        
// $(':reset') 匹配所有重置按钮

4548        
reset: function( elem ) {

4549             var name =
elem.nodeName.toLowerCase();

4550             return (name === "input"
|| name === "button") && "reset" === elem.type;

4551        
},

4552        
// $(':button') 匹配所有按钮

4553        
button: function( elem ) {

4554             var name =
elem.nodeName.toLowerCase();

4555             return name === "input"
&& "button" === elem.type || name === "button";

4556        
},

4557        
// $(':input') 匹配所有 input、textarea、select、button 元素

4558        
input: function( elem ) {

4559             return
(/input|select|textarea|button/i).test( elem.nodeName );

4560        
},

4561        
// $(':focus') 匹配当前焦点元素

4562        
focus: function( elem ) {

4563             return elem ===
elem.ownerDocument.activeElement;

4564        
}

4565    
},

 

4749 };

3.9.6 Sizzle.selectors.setFilters

对象Sizzle.selectors.setFilters中定义了一组位置伪类和对应的伪类过滤函数,称为“位置伪类过滤函数集”。支持的位置伪类有::first、:last、:even、:odd、:lt(index)、:gt(index)、:nth(index)、

:eq(index)。方法调用链为:Sizzle.filter()→Sizzle.selectors.filter.POS()→Sizzle.selectors.setFilters,如图3-1所示。

位置伪类过滤函数通过比较下标来确定元素在集合中的位置,返回一个布尔值,其参数格式为:

Sizzle.selectors.setFilters[ 位置伪类 ]( 元素, 下标, 正则匹配结果, 元素集合 );

// 正则匹配结果是正则
Sizzle.selectors.match.POS 匹配块选择器表达式的结果,含有 2 个分组:位置伪类、位置伪类参数

相关代码如下所示,为了方便解释,代码中增加了示例和注释:

4221 var Expr =
Sizzle.selectors = {

 

4566    
setFilters: {

          
   // $(':first') 匹配找到的第一个元素

4567          first: function( elem, i ) {

4568              return i === 0;

4569          },

4570          // $(':last') 匹配找到的最后一个元素

4571          last: function( elem, i, match, array
) {

4572              return i === array.length - 1;

4573          },

4574          // $(':even') 匹配所有下标为偶数的元素,从0开始计数

4575          even: function( elem, i ) {

4576              return i % 2 === 0;

4577          },

4578          // $(':odd') 匹配所有下标为奇数的元素,从0开始计数

4579          odd: function( elem, i ) {

4580              return i % 2 === 1;

4581          },

4582          // $(':lt(index)') 匹配所有小于指定下标的元素

4583          lt: function( elem, i, match ) {

4584              return i < match[3] - 0;

4585          },

4586          // $(':gt(index)') 匹配所有大于指定下标的元素

4587          gt: function( elem, i, match ) {

4588              return i > match[3] - 0;

4589          },

4590          // $(':nth(index)') 匹配一个指定下标的元素,从
0 开始计数

4591          nth: function( elem, i, match ) {

4592              return match[3] - 0 === i;

4593          },

4594          // $(':eq(index)') 匹配一个指定下标的元素,从
0 开始计数

4595          eq: function( elem, i, match ) {

4596              return match[3] - 0 === i;

4597          }

4598     
},

 

4749 };

3.9.7 Sizzle.selectors.filter

对象Sizzle.selectors.filter中定义了类型PSEUDO、CHILD、ID、TAG、CLASS、ATTR、POS所对应的过滤函数,称为“过滤函数集”。方法调用链为:Sizzle.filter()→Sizzle.selectors.filter[ type ],如图3-1和图3-6所示。

过滤函数负责检查元素是否匹配过滤表达式,返回一个布尔值,其参数格式为:

Sizzle.selectors.filter[ 类型 ]( 元素, 正则匹配结果或过滤表达式,
下标, 元素集合 )

// 正则匹配结果指
Sizzle.selectors.match 中对应的正则匹配块表达式的结果

// 过滤表达式指经过
Sizzle.selectors.preFilter 处理后的块表达式

Sizzle.selectors.filter的总体源码结构如下所示:

var Expr = Sizzle.selectors = {

  
filter: {

    
PSEUDO: function( elem, match, i, array )  { ... },

    
CHILD: function( elem, match )  {
... },

    
ID: function( elem, match )  { ...
},

    
TAG: function( elem, match )  {
... },

    
CLASS: function( elem, match )  {
... },

    
ATTR: function( elem, match )  {
... },

    
POS: function( elem, match, i, array ) 
{ ... }

   }

};

下面对其中的过滤函数逐个进行介绍和分析。

1.?PSEUDO

伪类过滤函数Sizzle.selectors.filter.PSEUDO( elem, match, i, array )用于检查元素是否匹配伪类。大部分检查通过调用伪类过滤函数集Sizzle.selectors.filters中对应的伪类过滤函数来实现,对于伪类:contains(text)、:not(selector)则做特殊处理。具体请参见3.9.5节。

相关代码如下所示:

4221 var Expr =
Sizzle.selectors = {

 

4599    
filter: {

4600        
PSEUDO: function( elem, match, i, array ) {

4601             var name = match[1],

4602                 filter = Expr.filters[ name ];

4603

4604             if ( filter ) {

4605                 return filter( elem, i, match, array );

4606

4607             } else if ( name ===
"contains" ) {

4608                 return (elem.textContent ||
elem.innerText || getText([ elem ]) || "").indexOf(match[3]) > =
0;

4609

4610             } else if ( name ===
"not" ) {

4611                 var not = match[3];

4612

4613                 for ( var j = 0, l =
not.length; j < l; j++ ) {

4614                     if ( not[j] === elem ) {

4615                         return false;

4616                     }

4617                 }

4618

4619                 return true;

4620

4621             } else {

4622                 Sizzle.error( name );

4623             }

4624        
},

 

4748    
}

 

4749 };

第4600行:参数match是正则Sizzle.selectors.match.PSEUDO匹配块表达式的结果,含有3个分组:伪类、引号、伪类参数。

第4602~4605行:如果在伪类过滤函数集Sizzle.selectors.filters中存在对应的伪类过滤函数,则调用它来检查元素是否匹配伪类。

第4607~4608行:伪类:contains(text)用于匹配包含指定文本的所有元素。如果伪类是:contains( text ),则先取出当前元素的文本内容,然后调用字符串方法indexOf()检查是否含有指定的文本。

第4610~4619行:伪类:not(selector)用于匹配与指定选择器不匹配的所有元素。如果伪类是:not(selector),则检查当前元素是否与match[3]中的某个元素相等,如果相等则返回false,否则返回true。在预过滤函数Sizzle.selectors.preFilter.PSEUDO中,对于伪类:not

(selector),会将match[3]替换为其匹配的元素集合。

第4621~4623行:对于不支持的伪类,一律调用方法Sizzle.error( msg )抛出语法错误。方法Sizzle.error( msg )请参见3.10.4节。

2.?CHILD

子元素伪类过滤函数Sizzle.selectors.filter.CHILD( elem, match )用于检查元素是否匹配子元素伪类。支持的子元素伪类如表3-14所示。

表3-14 子元素伪类

序  号         子元素伪类     说  明

1       :nth-child(index/even/odd/equation)       匹配父元素下的第N个子元素或奇偶元素

2       :first-child         匹配父元素的第一个子元素

3       :last-child          匹配父元素的最后一个子元素

4       :only-child         如果某个元素是父元素的唯一子元素,则匹配;如果父元素还含有多个子元素,则不匹配

 

相关代码如下所示:

4221 var Expr =
Sizzle.selectors = {

 

4599    
filter: {

 

4626        
CHILD: function( elem, match ) {

4627             var first, last,

4628                 doneName, parent, cache,

4629  
              count, diff,

4630                 type = match[1],

4631                 node = elem;

4632

4633             switch ( type ) {

4634                 case "only":

4635                 case "first":

4636                     while ( (node = node.previousSibling)
)  {

4637                         if ( node.nodeType ===
1 ) {

4638                             return false;

4639                         }

4640                     }

4641

4642                     if ( type ===
"first" ) {

4643       
                 return true;

4644                     }

4645

4646                     node = elem;

4647

4648                 case "last":

4649                     while ( (node =
node.nextSibling) )  {

4650                         if ( node.nodeType ===
1 ) {

4651                             return false;

4652                         }

4653                     }

4654

4655                     return true;

4656

4657                 case "nth":

4658   
                 first = match[2];

4659                     last = match[3];

4660

4661                     if ( first === 1
&& last === 0 ) {

4662                         return true;

4663                     }

4664                    

4665                     doneName = match[0];

4666                     parent = elem.parentNode;

4667    

4668                     if ( parent &&
(parent[ expando ] !== doneName || !elem.nodeIndex) ) {

4669                         count = 0;

4670                        

4671                         for ( node =
parent.firstChild; node; node = node.nextSibling ) {

4672                             if ( node.nodeType
=== 1 ) {

4673                                 node.nodeIndex
= ++count;

4674                             }

4675                         }

4676

4677                         parent[ expando ] =
doneName;

4678                     }

4679                    

4680                     diff = elem.nodeIndex -
last;

4681

4682                     if ( first === 0 ) {

4683                         return diff === 0;

4684

4685                     } else {

4686                         return ( diff % first
=== 0 && diff / first >= 0 );

4687                     }

4688             }

4689        
},

 

4748    
}

 

4749 };

第4634~4655行:如果伪类是:only-child,则检查当前元素之前(previousSibling)和之后(nextSibling)是否有兄弟元素,如果都没有则返回true,否则返回false。注意这里的分支only是通过分支first和分支last实现的。

第4635~4644行:如果伪类是:first-child,则检查当前元素之前(previousSibling)是否有兄弟元素,有则返回false,没有则返回true。

第4648~4655行:如果伪类是:last-child,则检查当前元素之后(nextSibling)是否有兄弟元素,有则返回false,没有则返回true。

第4657~4687行:如果伪类是:nth-child(index/even/odd/equation),则检查当前元素的下标是否匹配伪类参数,检测公式为:

( 当前元素在其父元素中的下标位置 -
last ) % first === 0

在预过滤函数Sizzle.selectors.preFilter.CHILD中已将伪类参数统一格式化为first*n+last,例如,odd格式化为2n+1,其中,first存储在match[2]中,last存储在match[3]中。

第4665~4678行:找到当前元素的父元素,然后为每个子元素设置属性nodeIndex,从而标识出每个子元素的下标位置。如果父元素未被本次过滤标识过,或当前元素未被标识过,才会为子元素设置属性nodeIndex,以确保只会标识一次。

match[0]是本次过滤的唯一标识,在执行子元素预过滤函数Sizzle.selectors.preFilter.CHILD( match )时被分配,具体请参见3.9.4节。

3.?ID

ID过滤函数Sizzle.selectors.filter.ID(
elem, match )用于检查元素的属性id是否与指定的id相等。

相关代码如下所示:

4221 var Expr =
Sizzle.selectors = {

 

4599    
filter: {

 

4691        
ID: function( elem, match ) {

4692             return elem.nodeType === 1
&& elem.getAttribute("id") === match;

4693        
},

 

4748    
}

 

4749 };

4.?TAG

标签过滤函数Sizzle.selectors.filter.TAG( elem, match )用于检查元素的标签名nodeName是否与指定的标签名相等。

相关代码如下所示:

4221 var Expr =
Sizzle.selectors = {

 

4599    
filter: {

 

4691        
ID: function( elem, match ) {

4692             return elem.nodeType === 1
&& elem.getAttribute("id") === match;

4693        
},

 

4748    
}

 

4749 };

5.?CLASS

类样式过滤函数Sizzle.selectors.filter.CLASS( elem, match )用于检查元素的类样式className是否含有指定的类样式。检查技巧是在类样式前后加空格,然后判断字符串方法indexOf()的返回值。

相关代码如下所示:

4221 var Expr = Sizzle.selectors
= {

 

4599    
filter: {

 

4699        
CLASS: function( elem, match ) {

4700             return (" " +
(elem.className || elem.getAttribute("class")) + " ")

4701                 .indexOf( match ) > -1;

4702        
},

 

4748    
}

 

4749 };

6.ATTR

属性过滤函数Sizzle.selectors.filter.ATTR( elem, match )用于检查元素的属性是否匹配属性表达式。支持的属性表达式如表3-15所示。

表3-15 属性表达式

序  号         属性表达式     说  明

1       [attribute]        匹配含有指定属性的元素

2       [attribute=value]     匹配含有指定属性,并且当前属性值等于指定值的元素

3       [attribute!=value]    匹配不包含指定属性,或者当前属性值不等于指定值的元素

4       [attribute^=value]   匹配含有指定属性,并且属性值以指定值开始的元素

5       [attribute$=value]   匹配含有指定属性,并且当前属性值以指定值结束的元素

6       [attribute*=value]   匹配含有指定属性,并且当前属性值包含指定值的元素

7       [attribute|="value"]         匹配含有指定属性,并且当前属性值等于指定值,或者当前属性值以指定值开头,并且后跟一个连字符(-)的元素

8       [attribute~="value"]        匹配含有指定属性,并且当前属性值含有指定单词的元素。单词之间用空格分隔

 

相关代码如下所示:

4221 var Expr =
Sizzle.selectors = {

 

4599    
filter: {

 

4704        
ATTR: function( elem, match ) {

4705             var name = match[1],

4706                 result = Sizzle.attr ?

4707                     Sizzle.attr( elem, name )
:

4708                     Expr.attrHandle[ name ] ?

4709                     Expr.attrHandle[ name ](
elem ) :

4710                     elem[ name ] != null ?

4711                         elem[ name ] :

4712                         elem.getAttribute(
name ),

4713                 value = result + "",

4714                 type = match[2],

4715                 check = match[4];

4716

4717             return result == null ?

4718                 type === "!=" :

4719                 !type && Sizzle.attr ?

4720  
              result != null :

4721                 type === "=" ?

4722                 value === check :

4723                 type === "*=" ?

4724                 value.indexOf(check) >= 0 :

4725                 type === "~=" ?

4726                 (" " + value +
" ").indexOf(check) >= 0 :

4727                 !check ?

4728                 value && result !==
false :

4729                 type === "!=" ?

4730                 value !== check :

4731                 type === "^=" ?

4732                 value.indexOf(check) === 0 :

4733                 type === "$=" ?

4734                 value.substr(value.length -
check.length) === check :

4735                 type === "|=" ?

4736                 value === check ||
value.substr(0, check.length + 1) === check + "-" :

4737                 false;

4738        
},

 

4748    
}

 

4749 };

第4705行:变量name是指定的属性名。

第4706~4712行:变量result是元素的HTML属性值或DOM属性值。在jQuery中,因为Sizzle.attr()等价于jQuery.attr(),因此总是返回HTML属性,所以变量result也总是HTML属性值;在独立使用Sizzle时,则是先尝试读取DOM属性值,如果不存在才会读取HTML属性值。

第4713~4715行:变量value是变量result字符串格式;变量type是属性表达式的等号部分,例如,=、!=;变量check是指定的属性值。

第4717~4737行:根据等号部分,采用不同的比较方式来检查元素是否匹配属性表达式。由于这段复合三元表达式太长太复杂,因此,下面将格式稍做调整并加上注释,以便于阅读理解:

// [name!=value] 不包含指定属性

return result == null ? type ===
"!=" :

   //
[name] 包含指定属性

  
!type && Sizzle.attr ? result != null :

   //
[name=check] 包含指定属性,属性值等于指定值

  
type === "=" ? value === check :

   //
[name*=check] 含有指定属性,属性值包含指定值

  
type === "*=" ? value.indexOf(check) >= 0 :

   //
[name~="value"] 含有指定属性,属性值含有指定单词

  
type === "~=" ? (" " + value + "
").indexOf(check) >= 0 :

   //
如果没有指定值 check,只有指定属性值,并且属性值不是 false,才会返回 true

  
!check ? value && result !== false :

   //
以下均有指定值 check

   //
[name!=check] 含有指定属性,属性值不等于指定值

  
type === "!=" ? value !== check :

   //
[name^=check] 含有指定属性,属性值以指定值开始

  
type === "^=" ? value.indexOf(check) === 0 :

   //
[name$=check] 含有指定属性,属性值以指定值结束

  
type === "$=" ? value.substr(value.length - check.length) ===
check :

   //
[name|=check] 含有指定属性,属性值等于指定值,或者以指定值开头,且后跟一个连字符(-)

  
type === "|=" ? value === check || value.substr(0,
check.length + 1) === check + "-" :

  
false;

7.?POS

位置伪类过滤函数Sizzle.selectors.filter.POS( elem, match, i, array )用于检查元素是否匹配位置伪类,该函数通过调用位置伪类过滤函数集Sizzle.selectors.setFilters中对应的位置伪类过滤函数来实现,具体请参见3.9.6节。调用关系如图3-1所示。

相关代码如下所示:

4221 var Expr =
Sizzle.selectors = {

 

4599    
filter: {

 

4740      
POS: function( elem, match, i, array ) {

4741          var name = match[2],

4742             filter = Expr.setFilters[ name ];

4743

4744          if ( filter ) {

4745             return filter( elem, i, match,
array );

4746          }

4747     
}

4748   
}

 

4749 };

时间: 2024-09-27 01:55:03

jQuery技术内幕:深入解析jQuery架构设计与实现原理. 3.9 Sizzle.selectors的相关文章

jQuery技术内幕:深入解析jQuery架构设计与实现原理1

jQuery技术内幕:深入解析jQuery架构设计与实现原理 高 云 著 图书在版编目(CIP)数据 jQuery技术内幕:深入解析jQuery架构设计与实现原理 / 高云著. -北京:机械工业出版社,2013.11 ISBN 978-7-111-44082-6 I. j- II. 高- III. JAVA语言-程序设计 IV. TP312 中国版本图书馆CIP数据核字(2013)第221662号 版权所有·侵权必究 封底无防伪标均为盗版 本书法律顾问 北京市展达律师事务所     本书由阿里巴

jQuery技术内幕:深入解析jQuery架构设计与实现原理2

第三部分 底层支持模块 第3章 选择器Sizzle 第4章 异步队列Deferred Object 第5章 数据缓存Data  第6章 队列Queue 第7章 浏览器功能测试Support 第3章 选择器Sizzle Sizzle是一款纯JavaScript实现的CSS选择器引擎,它具有以下特性: 完全独立,无库依赖. 相较于大多数常用选择器其性能非常有竞争力. 压缩和开启gzip后只有4?KB. 具有高扩展性和易于使用的API. 支持多种浏览器,如IE 6.0+.Firefox 3.0+.Ch

基于微服务和Docker容器技术的PaaS云平台架构设计

本文讲的是基于微服务和Docker容器技术的PaaS云平台架构设计[编者的话]在系统架构上,PaaS云平台主要分为微服务架构.Docker容器技术.DveOps三部分,这篇文章重点介绍微服务架构的实施. [3 天烧脑式容器存储网络训练营 | 深圳站]本次培训以容器存储和网络为主题,包括:Docker Plugin.Docker storage driver.Docker Volume Pulgin.Kubernetes Storage机制.容器网络实现原理和模型.Docker网络实现.网络插件.

基于云技术的ELC集群式服务器架构设计与实现

基于云技术的ELC集群式服务器架构设计与实现 西安电子科技大学 张文 本文提出了一种基于云技术的弹性负载均衡集群式服务器架构(Elastic and Load Balancing Cluster Server Architecture based on CloudTechnology,ELC集群式服务器架构).本架构以Eucalyptus云计算平台为基础设施,按照计算与存储分离原则整体分为两部分.其中,计算服务系统在Eucalyptus云平台的计算模块基础上,通过虚拟服务器实例方式对外提供服务,

jQuery技术内幕:深入解析jQuery架构设计与实现原理. 导读

   本书由阿里巴巴资深前端开发工程师撰写,从源代码角度全面而系统地解读了jQuery的17个模块的架构设计理念和内部实现原理,旨在帮助读者参透jQuery中的实现技巧和技术精髓,同时本书也对广大开发者如何通过阅读源代码来提升编码能力和软件架构能力提供了指导.     本书首先通过"总体架构"梳理了各个模块的分类.功能和依赖关系,让大家对jQuery的工作原理有大致的印象:进而通过"构造jQuery对象"章节分析了构造函数jQuery()的各种用法和内部构造过程:接

jQuery技术内幕:深入解析jQuery架构设计与实现原理. 2.8 静态属性和方法

2.8 静态属性和方法 在构造jQuery对象模块中还定义了一些重要的静态属性和方法,它们是其他模块实现的基础.其整体源码结构如代码清单2-3所示. 代码清单2-3 静态属性和方法 388 jQuery.extend({ 389     noConflict: function( deep ) {}, 402     isReady: false, 406     readyWait: 1, 409     holdReady: function( hold ) {}, 418     read

jQuery技术内幕:深入解析jQuery架构设计与实现原理. 2.2 总体结构

2.2 总体结构 构造jQuery对象模块的总体源码结构如代码清单2-1所示. 代码清单2-1 构造 jQuery 对象模块的总体源码结构  16 (function( window, undefined ) {         // 构造 jQuery 对象  22    var jQuery = (function() {  25       var jQuery = function( selector, context ) {  27              return new jQ

jQuery技术内幕:深入解析jQuery架构设计与实现原理. 3.4 Sizzle( selector, context, results, seed )

3.4 Sizzle( selector, context, results, seed ) 函数Sizzle( selector, context, results, seed )用于查找与选择器表达式selector匹配的元素集合.该函数是选择器引擎的入口. 函数Sizzle( selector, context, results, seed )执行的6个关键步骤如下: 1)解析块表达式和块间关系符. 2)如果存在位置伪类,则从左向右查找: a.?查找第一个块表达式匹配的元素集合,得到第一个

jQuery技术内幕:深入解析jQuery架构设计与实现原理. 3.13 总结

3.13 总结 在本章中,对选择器引擎Sizzle做了完整的介绍和分析,总体源码结构见代码清单3-1,方法功能和调用关系见图3-1.在本章的最后还介绍和分析了jQuery对Sizzle的整合和扩展. 选择器表达式由块表达式和块间关系符组成.块表达式分为3种:简单表达式.属性表达式.伪类表达式:块间关系符分为4种:">"父子关系.""祖先后代关系."+"紧挨着的兄弟元素."-"之后的所有兄弟元素:块表达式和块间关系符组成了