jquery Selector 源码分析

  1. /**  
  2.  * author:prk  
  3.  * date:2008-08-04  
  4.  * comment:comment for selector of jQuery  
  5.  *   
  6.  */  
  7. var chars = jQuery.browser.safari && parseInt(jQuery.browser.version) < 417  
  8.         ? "(?:[\\w*_-]|\\\\.)"  
  9.         : "(?:[\\w\u0128-\uFFFF*_-]|\\\\.)", quickChild = new RegExp("^>\\s*("  
  10.         + chars + "+)"), quickID = new RegExp("^(" + chars + "+)(#)(" + chars   
  11.         + "+)"), // ^((?:[\\w*_-]|\\\\.))(#)((?:[\\w*_-]|\\\\.))   
  12. quickClass = new RegExp("^([#.]?)(" + chars + "*)");// ^([#.]?)((?:[\\w*_-]|\\\\.)*)   
  13.   
  14. jQuery.extend( {   
  15.     expr : {   
  16.         "" : function(a, i, m) {   
  17.             return m[2] == "*" || jQuery.nodeName(a, m[2]);   
  18.         },   
  19.         "#" : function(a, i, m) {   
  20.             return a.getAttribute("id") == m[2];   
  21.         },   
  22.         ":" : {   
  23.             // Position Checks   
  24.         lt : function(a, i, m) {   
  25.             return i < m[3] - 0;   
  26.         },   
  27.         gt : function(a, i, m) {   
  28.             return i > m[3] - 0;   
  29.         },   
  30.         nth : function(a, i, m) {   
  31.             return m[3] - 0 == i;   
  32.         },   
  33.         eq : function(a, i, m) {   
  34.             return m[3] - 0 == i;   
  35.         },   
  36.         first : function(a, i) {   
  37.             return i == 0;   
  38.         },   
  39.         last : function(a, i, m, r) {   
  40.             return i == r.length - 1;   
  41.         },   
  42.         even : function(a, i) {   
  43.             return i % 2 == 0;   
  44.         },   
  45.         odd : function(a, i) {   
  46.             return i % 2;   
  47.         },   
  48.   
  49.         // Child Checks   
  50.         "first-child" : function(a) {   
  51.             return a.parentNode.getElementsByTagName("*")[0] == a;   
  52.         },   
  53.         "last-child" : function(a) {   
  54.             return jQuery.nth(a.parentNode.lastChild, 1"previousSibling") == a;   
  55.         },   
  56.         "only-child" : function(a) {   
  57.             return !jQuery.nth(a.parentNode.lastChild, 2"previousSibling");   
  58.         },   
  59.   
  60.         // Parent Checks   
  61.         parent : function(a) {   
  62.             return a.firstChild;   
  63.         },   
  64.         empty : function(a) {   
  65.             return !a.firstChild;   
  66.         },   
  67.   
  68.         // Text Check   
  69.         contains : function(a, i, m) {   
  70.             return (a.textContent || a.innerText || jQuery(a).text() || "")   
  71.                     .indexOf(m[3]) >= 0;   
  72.         },   
  73.   
  74.         // Visibility   
  75.         visible : function(a) {   
  76.             return "hidden" != a.type && jQuery.css(a, "display") != "none"  
  77.                     && jQuery.css(a, "visibility") != "hidden";   
  78.         },   
  79.         hidden : function(a) {   
  80.             return "hidden" == a.type || jQuery.css(a, "display") == "none"  
  81.                     || jQuery.css(a, "visibility") == "hidden";   
  82.         },   
  83.   
  84.         // Form attributes   
  85.         enabled : function(a) {   
  86.             return !a.disabled;   
  87.         },   
  88.         disabled : function(a) {   
  89.             return a.disabled;   
  90.         },   
  91.         checked : function(a) {   
  92.             return a.checked;   
  93.         },   
  94.         selected : function(a) {   
  95.             return a.selected || jQuery.attr(a, "selected");   
  96.         },   
  97.   
  98.         // Form elements   
  99.         text : function(a) {   
  100.             return "text" == a.type;   
  101.         },   
  102.         radio : function(a) {   
  103.             return "radio" == a.type;   
  104.         },   
  105.         checkbox : function(a) {   
  106.             return "checkbox" == a.type;   
  107.         },   
  108.         file : function(a) {   
  109.             return "file" == a.type;   
  110.         },   
  111.         password : function(a) {   
  112.             return "password" == a.type;   
  113.         },   
  114.         submit : function(a) {   
  115.             return "submit" == a.type;   
  116.         },   
  117.         image : function(a) {   
  118.             return "image" == a.type;   
  119.         },   
  120.         reset : function(a) {   
  121.             return "reset" == a.type;   
  122.         },   
  123.         button : function(a) {   
  124.             return "button" == a.type || jQuery.nodeName(a, "button");   
  125.         },   
  126.         input : function(a) {   
  127.             return /input|select|textarea|button/i.test(a.nodeName);   
  128.         },   
  129.   
  130.         // :has()   
  131.         has : function(a, i, m) {   
  132.             return jQuery.find(m[3], a).length;   
  133.         },   
  134.   
  135.         // :header   
  136.         header : function(a) {   
  137.             return /h\d/i.test(a.nodeName);   
  138.         },   
  139.   
  140.         // :animated   
  141.         animated : function(a) {   
  142.             return jQuery.grep(jQuery.timers, function(fn) {   
  143.                 return a == fn.elem;   
  144.             }).length;   
  145.         }   
  146.     }   
  147. },   
  148.   
  149. // The regular expressions that power the parsing engine   
  150. parse : [   
  151. // Match: [@value='test'], [@foo]   
  152.         /^(\[) *@?([\w:-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,   
  153.   
  154.         // Match: :contains('foo')   
  155.         /^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,   
  156.   
  157.         // Match: :even, :last-child, #id, .class   
  158.         new RegExp("^([:.#]*)(" + chars + "+)")],   
  159.   
  160. multiFilter : function(expr, elems, not) {   
  161.     var old, cur = [];   
  162.   
  163.     while (expr && expr != old) {// 存在且改变   
  164.         old = expr;   
  165.         var f = jQuery.filter(expr, elems, not);   
  166.         expr = f.t.replace(/^\s*,\s*/, "");   
  167.         cur = not ? elems = f.r : jQuery.merge(cur, f.r);   
  168.     }   
  169.   
  170.     return cur;   
  171. },   
  172.   
  173. find : function(t, context) {   
  174.     if (typeof t != "string")   
  175.         return [t];// 快速处理非字符表达式   
  176.   
  177.     if (context && context.nodeType != 1 && context.nodeType != 9)   
  178.         return [];// 确保context是DOM元素或document   
  179.   
  180.     context = context || document;// 缺省的context   
  181.   
  182.     // 初始化,ret:result, done:已经完成,last:上一次的t,nodeName:节点名,如p   
  183.     var ret = [context], done = [], last, nodeName;   
  184.   
  185.     while (t && last != t) {// t存在,且变化   
  186.         var r = []; // ret的tempValue   
  187.         last = t; // last:上一次的t   
  188.         t = jQuery.trim(t);// 去首尾空格   
  189.   
  190.         var foundToken = false, re = quickChild, // 以>开头的regexp   
  191.         m = re.exec(t);   
  192.   
  193.         if (m) {// 首先判断是不是以>开头   
  194.   
  195.             nodeName = m[1].toUpperCase();   
  196.   
  197.             // 找到上下文的那些满足regexp中nodeName的所有子节点。   
  198.             for (var i = 0;ret[i]; i++)   
  199.                 for (var c = ret[i].firstChild;c; c = c.nextSibling)   
  200.                     if (c.nodeType == 1  
  201.                             && (nodeName == "*" || c.nodeName.toUpperCase() == nodeName))   
  202.                         r.push(c);   
  203.   
  204.             ret = r; // 现在找到的所有元素都是上下文(context)   
  205.             t = t.replace(re, "");// remove已经处理过的部分   
  206.             if (t.indexOf(" ") == 0)// 说明是 "E F"这样的形式   
  207.                 continue;// 循环就能找到所有   
  208.   
  209.             foundToken = true;// 找到标识   
  210.   
  211.         } else {// 第二判断是不是以+~开头   
  212.   
  213.             re = /^([>+~])\s*(\w*)/i;   
  214.   
  215.             if ((m = re.exec(t)) != null) {// 以+~开头的   
  216.                 r = [];   
  217.                 var merge = {};   
  218.                 nodeName = m[2].toUpperCase();// 节点名   
  219.                 m = m[1];// 符号,如+,~   
  220.                 // 如果参数t匹配" "或>(子元素),由context的第一个子元素开始遍历,   
  221.                 // 如果参数t匹配~或+(后续元素),则从context的下一个元素开始遍历   
  222.                 for (var j = 0, rl = ret.length;j < rl; j++) {// 已经找到的节点(context)遍历   
  223.                     // 把~和+的操作统一在一起进行处理   
  224.                     var n = (m == "~" || m == "+"  
  225.                             ? ret[j].nextSibling   
  226.                             : ret[j].firstChild);   
  227.                     for (;n; n = n.nextSibling)   
  228.                         if (n.nodeType == 1) {// 保证节点是元素类型   
  229.                             var id = jQuery.data(n);// 为n元素生成全局唯一的id   
  230.   
  231.                             if (m == "~" && merge[id])// 保证ret中元素不重复   
  232.                                 break;// nextSibling会循环到第一个节点?   
  233.   
  234.                             if (!nodeName   
  235.                                     || n.nodeName.toUpperCase() == nodeName) {   
  236.                                 if (m == "~")// 找到元素保存起来   
  237.                                     merge[id] = true;   
  238.                                 r.push(n);   
  239.                             }   
  240.   
  241.                             if (m == "+")// 直接后续兄弟节点,只进行一次操作。   
  242.                                 break;   
  243.                         }   
  244.                 }   
  245.   
  246.                 ret = r;// 找到元素的后续操作   
  247.                 t = jQuery.trim(t.replace(re, ""));   
  248.                 foundToken = true;   
  249.             }   
  250.         }   
  251.   
  252.         if (t && !foundToken) {// 不是以>~+开头的   
  253.   
  254.             if (!t.indexOf(",")) {// ,分隔出现在第一个位置上   
  255.   
  256.                 if (context == ret[0])   
  257.                     ret.shift();// 把初始化给定的context清除出ret   
  258.                 done = jQuery.merge(done, ret);// ret的其它元素放入done   
  259.   
  260.                 r = ret = [context];// 重新初始化   
  261.   
  262.                 // Touch up the selector string   
  263.                 t = " " + t.substr(1, t.length);   
  264.   
  265.             } else {// 采用,分隔的多表达式   
  266.                 /*  
  267.                  * qId:^((?:[\w*_-]|\.)+)(#)((?:[\w*_-]|\.)+)  
  268.                  * qclass:^([#.]?)((?:[\\w*_-]|\.)*)  
  269.                  */  
  270.                 var re2 = quickID;// 如(.)nodeName#idName   
  271.                 var m = re2.exec(t);// 找到第一个相配的   
  272.   
  273.                 if (m) {   
  274.                     m = [0, m[2], m[3], m[1]];// m=[0,#,idName,nodeName]   
  275.                 } else {   
  276.                     re2 = quickClass;// #nodeName,.className   
  277.                     m = re2.exec(t);// m=[all,#,idName]   
  278.                 }   
  279.   
  280.                 m[2] = m[2].replace(/\\/g, "");// 去除转义字符   
  281.                 var elem = ret[ret.length - 1];// 结果数组最后一个   
  282.   
  283.                 // 根据Id直接找到元素并确保其的确存在   
  284.                 if (m[1] == "#" && elem && elem.getElementById   
  285.                         && !jQuery.isXMLDoc(elem)) {   
  286.                     var oid = elem.getElementById(m[2]);   
  287.   
  288.                     // 回测元素的ID的确存在,在IE中会取name属性的值,同时在form 元素中   
  289.                     // 会选中在form中元素的name属性为id的元素。   
  290.                     if ((jQuery.browser.msie || jQuery.browser.opera) && oid   
  291.                             && typeof oid.id == "string" && oid.id != m[2])   
  292.                         oid = jQuery('[@id="' + m[2] + '"]', elem)[0];   
  293.   
  294.                     // 回测元素的node Name是否相同,如div#foo,可以提交效率   
  295.                     ret = r = oid && (!m[3] || jQuery.nodeName(oid, m[3]))   
  296.                             ? [oid]   
  297.                             : [];   
  298.                 } else {// 找到结果集合中每个元素所有的后代元素组成集合,进行排查   
  299.                     for (var i = 0;ret[i]; i++) {   
  300.                         var tag = (m[1] == "#" && m[3] ? m[3] : (m[1] != ""  
  301.                                 || m[0] == "" ? "*" : m[2]));// 分情况取tagName   
  302.   
  303.                         if (tag == "*"  
  304.                                 && ret[i].nodeName.toLowerCase() == "object")   
  305.                             tag = "param";// Handle IE7 being really dumb   
  306.                         // about <object>s   
  307.   
  308.                         r = jQuery.merge(r, ret[i].getElementsByTagName(tag));   
  309.                     }   
  310.   
  311.                     if (m[1] == "."// 根据class在找到结果集合中过滤   
  312.                         r = jQuery.classFilter(r, m[2]);   
  313.   
  314.                     if (m[1] == "#") {// 对元素的id过滤,找到regexp给定id的元素   
  315.                         var tmp = [];   
  316.                         for (var i = 0;r[i]; i++)   
  317.                             if (r[i].getAttribute("id") == m[2]) {   
  318.                                 tmp = [r[i]];   
  319.                                 break;   
  320.                             }   
  321.   
  322.                         r = tmp;   
  323.                     }   
  324.   
  325.                     ret = r;   
  326.                 }   
  327.   
  328.                 t = t.replace(re2, "");   
  329.             }   
  330.   
  331.         }   
  332.   
  333.         if (t) {// 根据余下的selector,对找到的r集合中的元素进行过滤   
  334.             var val = jQuery.filter(t, r);   
  335.             ret = r = val.r;   
  336.             t = jQuery.trim(val.t);// 去首尾空格   
  337.         }   
  338.     }   
  339.   
  340.     if (t)// selector出现,返回[]。   
  341.         ret = [];   
  342.   
  343.     if (ret && context == ret[0])   
  344.         ret.shift();// 去掉根上下文   
  345.   
  346.     done = jQuery.merge(done, ret);// 合并   
  347.   
  348.     return done;   
  349. },   
  350. // 找到r中element中的className中含有m 或不含有的所有的元素   
  351. classFilter : function(r, m, not) {   
  352.     m = " " + m + " ";   
  353.     var tmp = [];   
  354.     for (var i = 0;r[i]; i++) {   
  355.         var pass = (" " + r[i].className + " ").indexOf(m) >= 0;   
  356.         if (!not && pass || not && !pass)   
  357.             tmp.push(r[i]);   
  358.     }   
  359.     return tmp;   
  360. },   
  361.   
  362. filter : function(t, r, not) {   
  363.     var last;   
  364.   
  365.     while (t && t != last) {// t存在,且改变   
  366.         last = t;   
  367.   
  368.         // Match: [@value='test'], [@foo]   
  369.         // 1、^(\[) *@?([\w:-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,   
  370.   
  371.         // Match: :contains('foo')   
  372.         // 2、^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,   
  373.   
  374.         // Match: :even, :last-child, #id, .class   
  375.         // 3、new RegExp("^([:.#]*)(" + chars + "+)")],   
  376.   
  377.         // isSimple = /^.[^:#\[\.]*$/   
  378.         var p = jQuery.parse, m;   
  379.   
  380.         for (var i = 0;p[i]; i++) {// 找到与jQuery.parse中regexp相配的   
  381.             m = p[i].exec(t);   
  382.             if (m) {   
  383.                 t = t.substring(m[0].length);   
  384.                 m[2] = m[2].replace(/\\/g, "");// 有可能会有没有转换的\去掉   
  385.                 break;   
  386.             }   
  387.         }   
  388.   
  389.         if (!m)// 与上面三种的regexp都不相配   
  390.             break;   
  391.   
  392.         // :not(.class) 处理不包含.class的其它的元素   
  393.         if (m[1] == ":" && m[2] == "not")   
  394.             // 性能上优化 m[3]是.class经常出现   
  395.             r = isSimple.test(m[3])   
  396.                     ? jQuery.filter(m[3], r, true).r   
  397.                     : jQuery(r).not(m[3]);   
  398.         else if (m[1] == ".")// 性能上优化考虑   
  399.             r = jQuery.classFilter(r, m[2], not);   
  400.         else if (m[1] == "[") {// [@value='test']形式的属性选择   
  401.             var tmp = [], type = m[3];// 符号,如=   
  402.   
  403.             for (var i = 0, rl = r.length;i < rl; i++) {   
  404.                 var a = r[i], z = a[jQuery.props[m[2]] || m[2]];// 元素的属性值   
  405.   
  406.                 if (z == null || /style|href|src|selected/.test(m[2]))   
  407.                     z = jQuery.attr(a, m[2]) || '';// 几个特殊的处理   
  408.   
  409.                 // [foo],[foo=aa][foo!=aa][foo^=aa][foo$=aa][foo~=aa]   
  410.                 if ((type == "" && !!z || type == "=" && z == m[5]   
  411.                         || type == "!=" && z != m[5] || type == "^=" && z   
  412.                         && !z.indexOf(m[5]) || type == "$="  
  413.                         && z.substr(z.length - m[5].length) == m[5] || (type == "*=" || type == "~=")   
  414.                         && z.indexOf(m[5]) >= 0)   
  415.                         ^ not)   
  416.                     tmp.push(a);   
  417.             }   
  418.             r = tmp;   
  419.   
  420.         } else if (m[1] == ":" && m[2] == "nth-child") {// 性能考量   
  421.             var merge = {}, tmp = [],   
  422.             // parse equations like 'even', 'odd', '5', '2n', '3n+2',   
  423.             // '4n-1', '-n+6'   
  424.             test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(m[3] == "even" && "2n"  
  425.                     || m[3] == "odd" && "2n+1" || !/\D/.test(m[3]) && "0n+"  
  426.                     + m[3] || m[3]),   
  427.   
  428.             // 计算器(first)n+(last),first=(-?)(\d*),last=((?:\+|-)?\d*)   
  429.             first = (test[1] + (test[2] || 1)) - 0, last = test[3] - 0;   
  430.   
  431.             for (var i = 0, rl = r.length;i < rl; i++) {   
  432.                 var node = r[i], parentNode = node.parentNode, id = jQuery   
  433.                         .data(parentNode);   
  434.   
  435.                 if (!merge[id]) {// 为元素的每个子节点标上顺序号,作了不重复标识   
  436.                     var c = 1;   
  437.                     for (var n = parentNode.firstChild;n; n = n.nextSibling)   
  438.                         if (n.nodeType == 1)   
  439.                             n.nodeIndex = c++;   
  440.                     merge[id] = true;   
  441.                 }   
  442.   
  443.                 var add = false;   
  444.   
  445.                 if (first == 0) {// 0不能作除数   
  446.                     if (node.nodeIndex == last)   
  447.                         add = true;   
  448.                 }   
  449.                 // 处理3n+2这种形式同时表达式要大于0   
  450.                 else if ((node.nodeIndex - last) % first == 0  
  451.                         && (node.nodeIndex - last) / first >= 0)   
  452.                     add = true;   
  453.   
  454.                 if (add ^ not)   
  455.                     tmp.push(node);   
  456.             }   
  457.   
  458.             r = tmp;   
  459.   
  460.         } else {// 根据m[1]m[2]在Query.expr找到对应的处理函数   
  461.             var fn = jQuery.expr[m[1]];   
  462.             if (typeof fn == "object")   
  463.                 fn = fn[m[2]];   
  464.   
  465.             if (typeof fn == "string")   
  466.                 fn = eval("false||function(a,i){return " + fn + ";}");   
  467.   
  468.             // 执行处理函数过滤r   
  469.             r = jQuery.grep(r, function(elem, i) {   
  470.                 return fn(elem, i, m, r);   
  471.             }, not);   
  472.         }   
  473.     }   
  474.   
  475.     // Return an array of filtered elements (r)   
  476.     // and the modified expression string (t)   
  477.     return {   
  478.         r : r,   
  479.         t : t   
  480.     };   
  481. },   
  482.   
  483. // dir:nextSibling elem:element   
  484. dir : function(elem, dir) {   
  485.     var matched = [], cur = elem[dir];   
  486.     while (cur && cur != document) {   
  487.         if (cur.nodeType == 1)   
  488.             matched.push(cur);   
  489.         cur = cur[dir];   
  490.     }   
  491.     return matched;   
  492. },   
  493. // dir:nextSibling result:deep cur:current. elem :no use   
  494. nth : function(cur, result, dir, elem) {   
  495.     result = result || 1;   
  496.     var num = 0;   
  497.   
  498.     for (;cur; cur = cur[dir])   
  499.         if (cur.nodeType == 1 && ++num == result)   
  500.             break;   
  501.   
  502.     return cur;   
  503. },   
  504.   
  505. // 排除elem的n的后续兄弟元素节点。   
  506. sibling : function(n, elem) {   
  507.     var r = [];   
  508.   
  509.     for (;n; n = n.nextSibling) {   
  510.         if (n.nodeType == 1 && n != elem)   
  511.             r.push(n);   
  512.     }   
  513.   
  514.     return r;   
  515. }   
  516. });  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值