zoukankan      html  css  js  c++  java
  • jQuery.API源码深入剖析以及应用实现(4) - 选择器篇(下)

    这篇继上篇的 jQuery.API源码深入剖析以及应用实现(3) - 选择器篇(上) 之后继续介绍选择器的其它原理,包括内容,可见性,属性,子元素,表单,表单对象属性等等原理。

    jQuery选择器的图示包括:


    一、内容

    1. 【 :contains(text)

    匹配包含给定文本的元素。

    例子

    HTML代码 jQuery代码 结果
    <div>John Resig</div>
    <div>George Martin</div>
    <div>Malcom John Sinclair</div>
    <div>J. Ohn </div>
    $("div:contains('John')") [ <div>John Resig</div>, <div>Malcom John Sinclair</div> ]

    首先我们先找到它的一个正则表达式

    PSEUDO: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/

    然后找到它的核心代码:

    filter: {
        PSEUDO: 
    function(elem, match, i, array){
            
    var name = match[1], filter = Expr.filters[ name ];

            
    if ( filter ) {
                
    return filter( elem, i, match, array );
            } 
    else if ( name === "contains" ) { 
                
    // textContext在FF下和innerText在IE下的属性是等效的,match[3]得到的是contains紧跟在后面包含的字符串,当elem元素的文本内容包含contains包含的关键字时,返回true
                return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
            } 
    else if ( name === "not" ) {
                
    var not = match[3];

                
    for ( var i = 0, l = not.length; i < l; i++ ) {
                    
    if ( not[i] === elem ) {
                        
    return false;
                    }
                }

                
    return true;
            }
        }
    }

    关键在于 (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0; 的布尔值判断,当返回真时,elem元素为匹配的元素。


    2. 【 :empty 】,【 :has(selector) 】,【 :parent

    :empty匹配所有不包含子元素或者文本的空元素。

    :has(selector) 匹配含有选择器所匹配的元素的元素。

    :parent匹配含有子元素或者文本的元素。

    例子

    HTML代码 jQuery代码 结果
    <table>
      <tr><td>Value 1</td><td></td></tr>
      <tr><td>Value 2</td><td></td></tr>
    </table>
    $("td:empty") [ <td></td>, <td></td> ]
    <div><p>Hello</p></div>
    <div>Hello again!</div>
    $("div:has(p)").addClass("test"); [ <div class="test"><p>Hello</p></div> ]
    <table>
      <tr><td>Value 1</td><td></td></tr>
      <tr><td>Value 2</td><td></td></tr>
    </table>
    $("td:parent")
    [ <td>Value 1</td>, <td>Value 1</td> ]

    同样它匹配正则表达式PSEUDO。

    找到它们的核心代码:

    filters: {
        parent: 
    function(elem){
            
    return !!elem.firstChild;
        },
        empty: 
    function(elem){
            
    return !elem.firstChild;
        },
        has: 
    function(elem, i, match){
            
    return !!Sizzle( match[3], elem ).length;
        }
    }

    其中当!elem.firstChild即elem元素不包含子节点或者文本元素时,empty返回真;

    当!!elem.firstChild即elem元素包含子节点或者文本元素时,parent返回真;

    has中,match[3]为has紧跟在后面含有的元素,如p元素,!!Sizzle( match[3], elem ).length 得到 match[3]元素中包含在elem元素中的个数,如果个数 > 1,has返回真。

    二、可见性

    1. 【 :hidden 】和【 :visible

    : hidden匹配所有的不可见元素,input 元素的 type 属性为 "hidden" 的话也会被匹配到。

    :visible匹配所有的可见元素。

    我们只需要看它的核心代码为:

    jQuery.expr = Sizzle.selectors;
    // …
    Sizzle.selectors.filters.hidden = function(elem){
        
    return "hidden" === elem.type ||
            jQuery.css(elem, 
    "display"=== "none" ||
            jQuery.css(elem, 
    "visibility"=== "hidden";
    };

    Sizzle.selectors.filters.visible 
    = function(elem){
        
    return "hidden" !== elem.type &&
            jQuery.css(elem, 
    "display"!== "none" &&
            jQuery.css(elem, 
    "visibility"!== "hidden";
    };

    当elem元素的CSS属性display为”none”,或者visibility为”hidden”时,返回真;

    当elem元素的CSS属性display不为”none”,并且visibility不为”hidden”时,返回真。

    通过布尔值来判断元素是否显示。

    三、属性

    1. [ attribute ],[ attribute=value ],[ attribute!=value ],[ attribute^=value ],[ attribute$=value ],[ attribute*=value ]

    属性匹配的正则表达式为:

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

    通过Sizzle.filter方法,得到ATTR的正则匹配,然后调用Expr.filter[ “ATTR” ],具体实现为:

    filters: {
        
    // 如 $("input[name^='news']")【<input name="newsletter" />】
        ATTR: function(elem, match){
            
    var result = Expr.attrHandle[ match[1] ] ? Expr.attrHandle[ match[1] ]( elem ) : elem[ match[1] ] || elem.getAttribute( match[1] ), value = result + "", type = match[2], check = match[4];
            
    return result == null ?
                type 
    === "!=" :
                type 
    === "=" ?
                value 
    === check :
                type 
    === "*=" ?
                value.indexOf(check) 
    >= 0 :
                type 
    === "~=" ?
                (
    " " + value + " ").indexOf(check) >= 0 :
                
    !match[4?
                result :
                type 
    === "!=" ?
                value 
    != check :
                type 
    === "^=" ?
                value.indexOf(check) 
    === 0 :
                type 
    === "$=" ?
                value.substr(value.length 
    - check.length) === check :
                type 
    === "|=" ?
                value 
    === check || value.substr(0, check.length + 1=== check + "-" :
                
    false;
        }
    }

    其中value相当于“newsletter”,check相当于“new”;
    可以看出“!=”和“=”判断value === check; 的布尔值,即value是否等于check;
    “^=”取得value.index(check) === 0; 的布尔值,即check的字符串是否在value的开头;
    “$=”取得value.substr(value.length - check.length) === check; 的布尔值,即check的字符串是否在value的末尾;
    “*=”取得value.index(check) >= 0; 的布尔值,即value包含check字符串为真;


    四、子元素

    1. 【 :nth-child(index/even/odd/equation) 】,【 :first-child 】,【 :last-child 】,【 :only-child

    :nth-child(index/even/odd/equation) 匹配其父元素下的第N个子或奇偶元素。
    :first-child 匹配第一个子元素。
    :last-child 匹配最后一个子元素。
    :only-child 如果某个元素是父元素中唯一的子元素,那将会被匹配。

    它匹配的正则表达式为:

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

    通过Sizzle.filter方法,得到CHILD的正则匹配,然后调用Expr.filter[ “CHILD” ],具体实现为:

    filter: {
            CHILD: 
    function(elem, match){
                
    var type = match[1], parent = elem.parentNode;

                
    var doneName = match[0];

                
    if ( parent && (!parent[ doneName ] || !elem.nodeIndex) ) {
                    
    var count = 1;

                    
    for ( var node = parent.firstChild; node; node = node.nextSibling ) {
                        
    if ( node.nodeType == 1 ) {
                            node.nodeIndex 
    = count++;
                        }
                    }

                    parent[ doneName ] 
    = count - 1;
                }

                
    if ( type == "first" ) {
                    
    return elem.nodeIndex == 1;
                } 
    else if ( type == "last" ) {
                    
    return elem.nodeIndex == parent[ doneName ];
                } 
    else if ( type == "only" ) {
                    
    return parent[ doneName ] == 1;
                } 
    else if ( type == "nth" ) {
                    
    var add = false, first = match[2], last = match[3];

                    
    if ( first == 1 && last == 0 ) {
                        
    return true;
                    }

                    
    if ( first == 0 ) {
                        
    // 形如 $("ul li:nth-child(2)")
                        if ( elem.nodeIndex == last ) {
                            add 
    = true;
                        }
                    } 
                    
    // 形如 $("ul li:nth-child(even)"), $("ul li:nth-child(odd)"),$("ul li::nth-child(3n+1)")
                    else if ( (elem.nodeIndex - last) % first == 0 && (elem.nodeIndex - last) / first >= 0 ) {
                        add 
    = true;
                    }

                    
    return add;
                }
            }
    }

    其中type为first时,elem.nodeIndex == 1; 当elem元素为第一个节点时,返回真;
    type为last时,elem.nodeIndex == parent[ doneName ]; 当elem元素为它的父节点的最后一个子节点时,返回真;
    type为only时,parent[ doneName ] == 1; 当elem元素的父节点只有一个子节点时,返回真;
    type为nth时,各种情况已经在代码中标注。

    五、表单

    1. 【 :input 】,【 :text 】,【 :password 】,【 :radio 】,【 :checkbox 】,【 :submit 】,【 :image 】,【 :reset 】,【 :button 】,【 :file 】,【 :hidden

    它们匹配的正则表达式为:

    PSEUDO: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/

    找到它们的核心代码:

        filters: {
            text: 
    function(elem){
                
    return "text" === elem.type;
            },
            radio: 
    function(elem){
                
    return "radio" === elem.type;
            },
            checkbox: 
    function(elem){
                
    return "checkbox" === elem.type;
            },
            file: 
    function(elem){
                
    return "file" === elem.type;
            },
            password: 
    function(elem){
                
    return "password" === elem.type;
            },
            submit: 
    function(elem){
                
    return "submit" === elem.type;
            },
            image: 
    function(elem){
                
    return "image" === elem.type;
            },
            reset: 
    function(elem){
                
    return "reset" === elem.type;
            },
            button: 
    function(elem){
                
    return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
            },
            input: 
    function(elem){
                
    return /input|select|textarea|button/i.test(elem.nodeName);
            }
        }

    可以看出elem.type得到元素的type属性,当元素type属性等于相应的值时,返回相应的布尔值。
    如果为真,最后返回匹配的jQuery对象。

    六、表单对象属性

    1. 【 : enabled 】,【 : disabled 】,【 : checked 】,【 :selected

    它们匹配的正则表达式为:

    PSEUDO: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/

    找到它们的核心代码:

        filters: {
            enabled: 
    function(elem){
                
    return elem.disabled === false && elem.type !== "hidden";
            },
            disabled: 
    function(elem){
                
    return elem.disabled === true;
            },
            checked: 
    function(elem){
                
    return elem.checked === true;
            },
            selected: 
    function(elem){
                
    // Accessing this property makes selected-by-default
                // options in Safari work properly
                elem.parentNode.selectedIndex;
                
    return elem.selected === true;
            }
        }

    其中,enabled对应elem的disabled属性为false并且elem的type属性为hidden;
    disabled对应elem的disabled属性为true;
    checked对应elem的checked属性为true;
    selected对应elem的selected属性为true;


    jQuery的选择器的原理至此已经全部介绍完了,通过选择器认识到了通过Expr.filters达到了过滤的目的。
    下篇我将介绍jQuery中的属性的原理。

    谢谢大家阅读

  • 相关阅读:
    OSCache报错error while trying to flush writer
    html 输入框验证
    Struts2 一张图片引发的bug
    Html 小插件10 即时新闻
    String
    内部类
    多态
    抽象&接口
    继承
    封装
  • 原文地址:https://www.cnblogs.com/liping13599168/p/1392954.html
Copyright © 2011-2022 走看看