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

    xiaoxiao2023-12-26  145

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

    相关资源:jQuery技术内幕 深入解析jQuery架构设计与实现原理
    最新回复(0)