你好,欢迎来到电脑编程技巧与维护杂志社! 杂志社简介广告服务读者反馈编程社区  
合订本订阅
 
 
您的位置:技术专栏 / Web开发
Jquery 深入学习之find()的理解
 
 前面find的实现的代码有点长。其实分析起来也不是很难。①②③④⑤处两个if-else是元素选择器,针对>/+~ F或#id,.class tagName,div#id这样的选择器进行查找元素,构成结果集。⑥实现的就是分析它之后的属性选择器进行筛选。这段代码说白就是select与Filte之间的交互分析CSS selector的字符串进行查找或筛选的过程。   
具体的细节在代码中有分析。在⑦处它采用先找到所有元素然后对每个元素进行class的判断来分析如.class这样selector。因为它在起始位说明是查找器。但是这里对这个最小的单元部分,我们还可能两个部分,找到所有元素,然后一个个排查,排查就是采用了jQuery.classFilter(r, m[2]):   
classFilter : function(r, m, not) {   
    m = " " + m + " ";//这里的做法就是怕出现如”class12”这样的问题   
    var tmp = [];      //如果传的m= class1,这个的条件是满足的,实际是不。   
    for (var i = 0;r[i]; i++) {   
        var pass = (" " + r[i].className + " ").indexOf(m) >= 0;   
        if (!not && pass || not && !pass)   
            tmp.push(r[i]);   
    }   
    return tmp;   
},   
jQuery.classFilter是比较简单。在find()的第二个部分是筛选,它调用jQuery.filter(t, r)来完成功能:   
  
//根据CSS selector表达式查找集合中满足该表达式的所有元素   
//还可以根据not来指定不满足CSS selector表达式元素集   
filter : function(t, r, not) {   
    var last;   
    while (t && t != last) {// t存在,且改变   
        last = t;   
        // Match: [@value='test'], [@foo]   
        // 1、^(\[)
*@?([\w:-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,   
  
        // Match: :contains('foo')   
        // 2、^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,   
  
        // Match: :even, :last-child, #id, .class   
        // 3、new RegExp("^([:.#]*)(" + chars + "+)")],         
           
        //这里可以看出我们直接调用filter的时候的selector如不是筛选器的话,   
        //那就不进行筛选了,这里的selector语法如[@value='test'], [@foo]、   
        //:contains('foo'),:even, :last-child, #id, .class的形式   
        //可以是上面这几种形式的组合,但不能包括元素选择器。   
    //而且复合的形式中间不能采用空格隔开,如[@value='test']#id.class可行的。   
        var p = jQuery.parse, m;   
        for (var i = 0;p[i]; i++) {// 找到与jQuery.parse中regexp相配的   
            m = p[i].exec(t);   
            if (m) {   
                t = t.substring(m[0].length);//删除处理过的字符部分   
                m[2] = m[2].replace(/\\/g, "");// 有可能会有没有转换的\去掉   
                break;   
            }   
        }           
        // 与上面三种的regexp都不相配   
        if (!m) break;   
  
        //处理 :not(.class)的形式,返回集合中不包含.class的其它的元素   
        if (m[1] == ":" && m[2] == "not")   
            // 性能上优化 m[3]是.class经常出现   
            r = isSimple.test(m[3])// isSimple = /^.[^:#\[\.]*$/   
                    ? jQuery.filter(m[3], r, true).r: jQuery(r).not(m[3]);   
                       
        //处理.class的过滤。只要看看m[2]这个class是不是被集合中元素的class包含。   
        else if (m[1] == ".")// 性能上优化考虑   
            r = jQuery.classFilter(r, m[2], not);   
            //处理属性过滤。如[@value='test']形式的属性选择   
        else if (m[1] == "[") {   
            var tmp = [], type = m[3];// 符号,如=   
  
            for (var i = 0, rl = r.length;i < rl; i++) {   
                //jQuery.props[m[2]]进行tag中属性名和对应的元素的属性名转换,   
                //因为tag中属性名是元素中简写,z取到 元素的属性值   
                var a = r[i], z = a[jQuery.props[m[2]] || m[2]];   
                   
                //直接取元素的属性值,没有取到,说明有的浏览器不支持这种方法   
                //进一步尝试采用jQuery.attr来进行非标准的兼容取属性值。   
                //就算是取到了值,但对于属性名为style|href|src|selected,   
                //它们不能直接取值,要进行特殊的处理,这个在jQuery.attr进行。   
                //其实这里可以直接采用jQuery.attr(a, m[2]),一步到位。   
                if (z == null || /style|href|src|selected/.test(m[2]))   
                    z = jQuery.attr(a, m[2]) || '';// 几个特殊的处理   
  
                //如果属性选择器满足这   
//[foo],[foo=aa][foo!=aa][foo^=aa][foo$=aa][foo~=aa]   
                //这几种方式之一,这个元素就可能通过。即满足条件。m[5]属性值。   
        if (   (type == "" && !!z//[foo]   
           || type == "=" && z == m[5]//[foo=aa]   
           || type == "!=" && z != m[5]//[foo!=aa]   
           || type == "^=" && z&& !z.indexOf(m[5])//[foo^=aa]   
           || type == "$="  && z.substr(z.length - m[5].length) == m[5]        || (type == "*=" || type == "~=")&& z.indexOf(m[5]) >= 0  
              )   
        ^ not)   
            tmp.push(a);   
            }   
            r = tmp;   
  
        }   
        //处理:nth-child(n+1)。其实这里也改变了结果集,   
        //不过这里是采用的是间接引用的方式,只要知道元素就可以了,   
        //不需要dom树去查找。因为它要解析参数中的表达式   
        else if (m[1] == ":" && m[2] == "nth-child") {// 性能考量   
            var merge = {}, tmp = [],   
        // 分析:nth-child(n+1)中的参数,这里支持   
        //'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'几种形式   
        //test[1]="-或空",test[2]="n前面的数或空",test[3]="n后面的数或空"   
        //这样把参数分成三个部分:1是负号的处理,2是xn中的x处理,3是n-x中-x的处理   
            //3中的是带有符号的。也就是+或-。   
            test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(m[3] == "even" && "2n"  
                    || m[3] == "odd" && "2n+1" || !/\D/.test(m[3]) && "0n+"  
                    + m[3] || m[3]),   
  
            // 计算(first)n+(last)   
            first = (test[1] + (test[2] || 1)) - 0, last = test[3] - 0;   
               
            //找到符合(first)n+(last)表达式的所有子元素   
        for (var i = 0, rl = r.length;i < rl; i++) {   
            var node = r[i], parentNode = node.parentNode,    
        id = jQuery.data(parentNode);//为该元素parentNode分配了一个全局的id   
  
            if (!merge[id]) {// 为元素的每个子节点标上顺序号,作了不重复标识   
                    var c = 1;   
                for (var n = parentNode.firstChild;n; n = n.nextSibling)   
                        if (n.nodeType == 1)n.nodeIndex = c++;   
                    merge[id] = true;   
                }   
  
                var add = false;//初始化add的标记   
                   
                //常数的形式,如1,2等等,当然还要判断元素的序号和这个数是否相等。   
                if (first == 0) {// 0不能作除数   
                    if (node.nodeIndex == last)   
                        add = true;   
                }   
                // 处理3n+2这种形式同时表达式要大于0   
                //当前的子节点的序号要满足两个条件:   
                //1、其序号进行first求余的结果=last.   
                //2、其序号要大于last。对于-n的形式,要大于-last.   
                else if ((node.nodeIndex - last) % first == 0  
                        && (node.nodeIndex - last) / first >= 0)   
                    add = true;   
  
                if (add ^ not)  tmp.push(node);   
            }   
  
            r = tmp;   
  
        }    
        else {// 根据m[1]m[2]在Query.expr找到对应的处理函数   
            var fn = jQuery.expr[m[1]];   
            //支持一个符号(如:last)后的方法名与函数的对应   
            if (typeof fn == "object")   
                fn = fn[m[2]];   
                
                //支持更简短的string来代替jQuery.expr中的funciton。   
                //这里没有用到。   
            if (typeof fn == "string")   
                fn = eval("false||function(a,i){return " + fn + ";}");   
  
        // 执行处理函数fn过滤。对于r中每个元素,如果fn返回的结果为true,保留下来。   
            r = jQuery.grep(r, function(elem, i) {   
                return fn(elem, i, m, r);   
            }, not);   
        }   
    }   
    return {   
        r : r,   
        t : t   
    };   
},   
jQuery.filter完成分析属性([])、Pseudo(:),class (.),id(#),的筛选的功能。从给定的集合中筛选出满足上面四种筛选表达式的集合。针对于find()。这个filter完成不表明整个selector的分析完成了。还会交替地通过查找器来查找或通过该函数来筛选。对于单独使用这个函数,表达式中就不应该含有查找的selector表达式了。筛选是根据[、:、#、.这四个符号来做为筛选器的分隔符的。   
class筛选器是通过classFilter来完成。它还把Pseudo中:not、:nth-child单独从Pseudo类的中单独提起出来处理。对于[的属性筛选器,实现起来也是很简单。除去这些,它还调用jQuery.expr[m[1]];来处理Pseudo类,而jQuery还做了扩展。jQuery.expr中的Pseudo类有以下几个部分:// Position Checks、// Child Checks、// Parent Checks、// Text Check、// Visibility、// Form attributes、// Form elements、// :has()、// :header、// :animated。也就是说在CSS selector中,我们可以采用Pseudo类提供上面这些类别的方法来进行筛选。其代码就是一些判断,不作分析。
  推荐精品文章

·2024年9月目录 
·2024年8月目录 
·2024年7月目录 
·2024年6月目录 
·2024年5月目录 
·2024年4月目录 
·2024年3月目录 
·2024年2月目录 
·2024年1月目录
·2023年12月目录
·2023年11月目录
·2023年10月目录
·2023年9月目录 
·2023年8月目录 

  联系方式
TEL:010-82561037
Fax: 010-82561614
QQ: 100164630
Mail:gaojian@comprg.com.cn

  友情链接
 
Copyright 2001-2010, www.comprg.com.cn, All Rights Reserved
京ICP备14022230号-1,电话/传真:010-82561037 82561614 ,Mail:gaojian@comprg.com.cn
地址:北京市海淀区远大路20号宝蓝大厦E座704,邮编:100089