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

相关文章
|
20天前
|
机器学习/深度学习 前端开发 Windows
【夯实技术基本功】「底层技术原理体系」全方位带你认识和透彻领悟正则表达式(Regular Expression)的开发手册(正则符号深入解析 )
【夯实技术基本功】「底层技术原理体系」全方位带你认识和透彻领悟正则表达式(Regular Expression)的开发手册(正则符号深入解析 )
31 0
|
27天前
|
设计模式 前端开发 测试技术
Flutter 项目架构技术指南
探讨Flutter项目代码组织架构的关键方面和建议。了解设计原则SOLID、Clean Architecture,以及架构模式MVC、MVP、MVVM,如何有机结合使用,打造优秀的应用架构。
Flutter 项目架构技术指南
|
24天前
|
存储 安全 编译器
【C++ 17 新功能 std::visit 】深入解析 C++17 中的 std::visit:从原理到实践
【C++ 17 新功能 std::visit 】深入解析 C++17 中的 std::visit:从原理到实践
69 0
|
20天前
|
设计模式 安全 Java
【分布式技术专题】「Tomcat技术专题」 探索Tomcat技术架构设计模式的奥秘(Server和Service组件原理分析)
【分布式技术专题】「Tomcat技术专题」 探索Tomcat技术架构设计模式的奥秘(Server和Service组件原理分析)
23 0
|
8天前
|
存储 中间件 关系型数据库
数据库切片大对决:ShardingSphere与Mycat技术解析
数据库切片大对决:ShardingSphere与Mycat技术解析
14 0
|
21天前
|
安全 Java 数据安全/隐私保护
【深入浅出Spring原理及实战】「EL表达式开发系列」深入解析SpringEL表达式理论详解与实际应用
【深入浅出Spring原理及实战】「EL表达式开发系列」深入解析SpringEL表达式理论详解与实际应用
44 1
|
20天前
|
存储 NoSQL 算法
【Redis技术进阶之路】「底层源码解析」揭秘高效存储模型与数据结构底层实现(字典)(二)
【Redis技术进阶之路】「底层源码解析」揭秘高效存储模型与数据结构底层实现(字典)
34 0
|
21天前
|
NoSQL Java Redis
【分布式技术专题】「分布式技术架构」手把手教你如何开发一个属于自己的分布式锁的功能组件(二)
【分布式技术专题】「分布式技术架构」手把手教你如何开发一个属于自己的分布式锁的功能组件
14 0
|
6天前
|
存储 人工智能 编译器
存算一体新兴力量:解析我国企业在存储创新、技术路径上的多元化探索
存算一体新兴力量:解析我国企业在存储创新、技术路径上的多元化探索

推荐镜像

更多