• 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. });  
  • 相关阅读:
    异步任务AsyncTask
    巧用TextView实现分隔线
    android系统的常用权限
    浅析对话框AlertDialog
    LinearLayout中的layout_weight属性详解
    CLOB大数据对象
    模糊查询demo
    ES6 箭头函数
    ES6中数组求和,求平均数方法( reduce )
    ES6中数组方法( every 和 some )
  • 原文地址:https://www.cnblogs.com/rooney/p/1346122.html
Copyright © 2020-2023  润新知