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 };