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