jQuery.API源码深入剖析以及应用实现(4) - 选择器篇(下)

继续介绍选择器的其它原理,包括内容,可见性,属性,子元素,表单,表单对象属性等等原理。

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达到了过滤的目的。

时间: 2024-09-25 02:35:31

jQuery.API源码深入剖析以及应用实现(4) - 选择器篇(下)的相关文章

jQuery.API源码深入剖析以及应用实现(3) - 选择器篇(上)

还漏了一个框题,jQuery的冲突机制解决方法jQuery.noConflict()以及jQuery.noConflict(extreme),这里先分析一下: jQuery.noConflict():运行这个函数将变量$的控制权让渡给第一个实现它的那个库. jQuery.noConflict(extreme):将$和jQuery的控制权都交还给原来的库. 比如在prototype框架中的$会和jQuery框架中的$产生命名冲突,这里就是为了解决这种问题. 现在先看下noConflict方法的具体

Portal Starter 源码深入剖析(一)

Portal Starter 源码深入剖析(一)学ASP.net光看书看来是不行的,找一些经典的源代码来读读,对提升认识是很有帮助的.在Microsoft的网站上找到几个范例,选择Portal是因为这个范例最大,可作为一个简单的门户站.Portal的工作流程:1.读取网站设置文件PortalCfg.xml至context中缓存起来,这个过程由Global.asax中的Application_BeginRequest()事件来完成的.2.客户访问Portal站,执行Default.aspx,Def

如何Debug JAVA api源码

问题描述 现在下载下来API源码了写了一个测试类,想debug看下源码是怎么实现的.例如:importjava.util.HashMap;publicclassTestHashMap{/***@paramargs*/publicstaticvoidmain(String[]args){HashMap<String,String>map=newHashMap<String,String>();map.put("name","username")

【深入浅出jQuery】源码浅析2--奇技淫巧

最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐进增强)优雅的处理能力以及 Ajax 等方面周到而强大的定制功能无不令人惊叹. 另外,阅读源码让我接触到了大量底层的知识.对原生JS .框架设计.代码优化有了全新的认识,接下来将会写一系列关于 jQuery 解析的文章. 我在 github 上关于 jQuery 源码的全文注解,感兴趣的可以围观一下

【深入浅出jQuery】源码浅析--整体架构

最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐进增强)优雅的处理能力以及 Ajax 等方面周到而强大的定制功能无不令人惊叹. 另外,阅读源码让我接触到了大量底层的知识.对原生JS .框架设计.代码优化有了全新的认识,接下来将会写一系列关于 jQuery 解析的文章. 网上已经有很多解读 jQuery 源码的文章了,作为系列开篇的第一篇,思前想去

tomcat集群实现源码级别剖析

随着互联网快速发展,各种各样供外部访问的系统越来越多且访问量越来越大,以前Web容器可以包揽接收-逻辑处理-响应整个请求生命周期的工作,现在为了构建让更多用户访问更强大的系统,人们通过不断地业务解耦.架构解耦将web容器的逻辑处理抽离交由其他中间件处理,例如缓存中间件.消息队列中间件.数据存储中间件等等.Web容器负责的工作可能越来越少,但是它确实必不可少的部分,它负责接收用户请求并分别调用各个服务最后响应.可以说目前最受欢迎的web容器是用Java写的tomcat小猫,由于生产上的tomcat

jQuery源码分析-03构造jQuery对象-源码结构和核心函数_jquery

作者:nuysoft/高云 QQ:47214707 EMail:nuysoft@gmail.com 毕竟是边读边写,不对的地方请告诉我,多多交流共同进步.本章还未写完,完了会提交PDF. 前记: 想系统的好好写写,但是会先从感兴趣的部分开始. 近期有读者把PDF传到了百度文库上,首先感谢转载和传播,但是据为已有并设置了挺高的财富值才能下载就不好了,以后我整理好了会传到文库上.请体谅一下. 3. 构造jQuery对象 3.1 源码结构 先看看总体结构,再做分解: 复制代码 代码如下: (funct

mahout源码分析之DistributedLanczosSolver(七) 总结篇

Mahout版本:0.7,hadoop版本:1.0.4,jdk:1.7.0_25 64bit. 看svd算法官网上面使用的是亚马逊的云平台计算的,不过给出了svd算法的调用方式,当算出了eigenVectors后,应该怎么做呢?比如原始数据是600*60(600行,60列)的数据,计算得到的eigenVectors是24*60(其中的24是不大于rank的一个值),那么最后得到的结果应该是original_data乘以eigenVectors的转置这样就会得到一个600*24的矩阵,这样就达到了

PHP调用TinyURL API源码示例

TinyURL是一个缩短网址的Web服务,可以把很长的网址变成简单的地址,通常创建TinyURL的方法是去其主页创建,有时候会有用户在客户端自动生成TinyURL的情况... TinyURL是一个缩短网址的Web服务,可以把很长的网址变成简单的地址,通常创建TinyURL的方法是去其主页创建,有时候会有用户在客户端自动生成TinyURL的情况,这里介绍一个通过PHP调用TinyURL生成缩短地址的方法. TinyURL API的PHP函数如下 <?php function TinyURL($u)