现在的位置: 首页 > 综合 > 正文

jquery Selector (修改)

2013年08月04日 ⁄ 综合 ⁄ 共 17631字 ⁄ 字号 评论关闭
  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 + "+)"),    
  11.         quickID = new RegExp("^(" + chars + "+)(#)(" + chars   
  12.         + "+)"), // ^((?:[\\w*_-]|\\\\.))(#)((?:[\\w*_-]|\\\\.))   
  13. quickClass = new RegExp("^([#.]?)(" + chars + "*)");// ^([#.]?)((?:[\\w*_-]|\\\\.)*)   
  14.   
  15. jQuery.extend( {   
  16.     expr : {   
  17.         "" : function(a, i, m) {   
  18.             return m[2] == "*" || jQuery.nodeName(a, m[2]);   
  19.         },   
  20.         "#" : function(a, i, m) {   
  21.             return a.getAttribute("id") == m[2];   
  22.         },   
  23.         ":" : {   
  24.             // Position Checks   
  25.         lt : function(a, i, m) {   
  26.             return i < m[3] - 0;   
  27.         },   
  28.         gt : function(a, i, m) {   
  29.             return i > m[3] - 0;   
  30.         },   
  31.         nth : function(a, i, m) {   
  32.             return m[3] - 0 == i;   
  33.         },   
  34.         eq : function(a, i, m) {   
  35.             return m[3] - 0 == i;   
  36.         },   
  37.         first : function(a, i) {   
  38.             return i == 0;   
  39.         },   
  40.         last : function(a, i, m, r) {   
  41.             return i == r.length - 1;   
  42.         },   
  43.         even : function(a, i) {   
  44.             return i % 2 == 0;   
  45.         },   
  46.         odd : function(a, i) {   
  47.             return i % 2;   
  48.         },   
  49.   
  50.         // Child Checks   
  51.         "first-child" : function(a) {   
  52.             return a.parentNode.getElementsByTagName("*")[0] == a;   
  53.         },   
  54.         "last-child" : function(a) {   
  55.             return jQuery.nth(a.parentNode.lastChild, 1"previousSibling") == a;   
  56.         },   
  57.         "only-child" : function(a) {   
  58.             return !jQuery.nth(a.parentNode.lastChild, 2"previousSibling");   
  59.         },   
  60.   
  61.         // Parent Checks   
  62.         parent : function(a) {   
  63.             return a.firstChild;   
  64.         },   
  65.         empty : function(a) {   
  66.             return !a.firstChild;   
  67.         },   
  68.   
  69.         // Text Check   
  70.         contains : function(a, i, m) {   
  71.             return (a.textContent || a.innerText || jQuery(a).text() || "")   
  72.                     .indexOf(m[3]) >= 0;   
  73.         },   
  74.   
  75.         // Visibility   
  76.         visible : function(a) {   
  77.             return "hidden" != a.type && jQuery.css(a, "display") != "none"  
  78.                     && jQuery.css(a, "visibility") != "hidden";   
  79.         },   
  80.         hidden : function(a) {   
  81.             return "hidden" == a.type || jQuery.css(a, "display") == "none"  
  82.                     || jQuery.css(a, "visibility") == "hidden";   
  83.         },   
  84.   
  85.         // Form attributes   
  86.         enabled : function(a) {   
  87.             return !a.disabled;   
  88.         },   
  89.         disabled : function(a) {   
  90.             return a.disabled;   
  91.         },   
  92.         checked : function(a) {   
  93.             return a.checked;   
  94.         },   
  95.         selected : function(a) {   
  96.             return a.selected || jQuery.attr(a, "selected");   
  97.         },   
  98.   
  99.         // Form elements   
  100.         text : function(a) {   
  101.             return "text" == a.type;   
  102.         },   
  103.         radio : function(a) {   
  104.             return "radio" == a.type;   
  105.         },   
  106.         checkbox : function(a) {   
  107.             return "checkbox" == a.type;   
  108.         },   
  109.         file : function(a) {   
  110.             return "file" == a.type;   
  111.         },   
  112.         password : function(a) {   
  113.             return "password" == a.type;   
  114.         },   
  115.         submit : function(a) {   
  116.             return "submit" == a.type;   
  117.         },   
  118.         image : function(a) {   
  119.             return "image" == a.type;   
  120.         },   
  121.         reset : function(a) {   
  122.             return "reset" == a.type;   
  123.         },   
  124.         button : function(a) {   
  125.             return "button" == a.type || jQuery.nodeName(a, "button");   
  126.         },   
  127.         input : function(a) {   
  128.             return /input|select|textarea|button/i.test(a.nodeName);   
  129.         },   
  130.   
  131.         // :has()   
  132.         has : function(a, i, m) {   
  133.             return jQuery.find(m[3], a).length;   
  134.         },   
  135.   
  136.         // :header   
  137.         header : function(a) {   
  138.             return /h\d/i.test(a.nodeName);   
  139.         },   
  140.   
  141.         // :animated   
  142.         animated : function(a) {   
  143.             return jQuery.grep(jQuery.timers, function(fn) {   
  144.                 return a == fn.elem;   
  145.             }).length;   
  146.         }   
  147.     }   
  148. },   
  149.   
  150. // The regular expressions that power the parsing engine   
  151. parse : [   
  152. // Match: [@value='test'], [@foo]   
  153.         /^(\[) *@?([\w:-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,   
  154.   
  155.         // Match: :contains('foo')   
  156.         /^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,   
  157.   
  158.         // Match: :even, :last-child, #id, .class   
  159.         new RegExp("^([:.#]*)(" + chars + "+)")],   
  160.   
  161. multiFilter : function(expr, elems, not) {   
  162.     var old, cur = [];   
  163.   
  164.     while (expr && expr != old) {// 存在且改变   
  165.         old = expr;   
  166.         var f = jQuery.filter(expr, elems, not);   
  167.         expr = f.t.replace(/^\s*,\s*/, "");   
  168.         cur = not ? elems = f.r : jQuery.merge(cur, f.r);   
  169.     }   
  170.   
  171.     return cur;   
  172. },   
  173.   
  174. find : function(t, context) {   
  175.     if (typeof t != "string")   
  176.         return [t];// 快速处理非字符表达式   
  177.     if (context && context.nodeType != 1 && context.nodeType != 9)   
  178.         return [];// 确保context是DOM元素或document   
  179.     context = context || document;// 缺省的context   
  180.     // 初始化,ret:result, done:已经完成,last:上一次的t,nodeName:节点名,如p   
  181.     var ret = [context], done = [], last, nodeName;   
  182.      
  183.     //这里就是把复合选择器的字符串从左到右取最小单元的选择进行分析操作   
  184.     //分析操作完之后就这个分析过的字符串部分给删除,   
  185.     //然后循环分析接下来的剩余的部分。直到字符串为空。   
  186.     //这里的最小单元指如#id,~F(+F,>F),.class,[id='xx'],F,:last()之类   
  187.     while (t && last != t) {// t存在,且变化   
  188.         var r = []; // ret的tempValue   
  189.         last = t; // last:上一次的t   
  190.         t = jQuery.trim(t);// 去首尾空格   
  191.   
  192.         var foundToken = false, re = quickChild, // 以>开头的regexp   
  193.         m = re.exec(t);   
  194.            
  195.        //这一部分处理了>,+,~的元素选择器。当然有的后代,有的兄弟选择的。   
  196.         if (m) {// 首先判断是不是以>开头,因为每次处理都处理都删除分析过的字符串部分 1   
  197.             //这里可以看作是>作为找到tagName元素的子节点们的标记   
  198.             nodeName = m[1].toUpperCase();//tagName   
  199.   
  200.             //在结果集中(第一次是默认是给定的context)找到满足的tagName元素的所有子节点。   
  201.             //两个循环,第一是对结果集中每个元素进行,第二个是对每个元素中每个子元素节点。   
  202.             //找到结果集中所有的元素的所有子元素的集合。   
  203.             for (var i = 0;ret[i]; i++)   
  204.                 for (var c = ret[i].firstChild;c; c = c.nextSibling)   
  205.                     if (c.nodeType == 1  
  206.                             && (nodeName == "*" || c.nodeName.toUpperCase() == nodeName))   
  207.                         r.push(c);   
  208.   
  209.             ret = r; // 现在找到的所有元素都是结果集   
  210.             t = t.replace(re, "");// remove已经处理过的字符串部分   
  211.                
  212.             //对于E (F,>F,+F etc)的形式,这里跳过后面的代码又回到while处执行。   
  213.             //但是在while处之后会把这个空格去掉。好像没有进行操作。这里变化了是ret。   
  214.             //无论后面是怎样的最小单元选择器,都是要根据这个个ret中的元素来进行操作。   
  215.             //如果是tagName,那么就是转4处执行ret[i].getElementsByTagName().   
  216.             //如果是>tagName,就执行1处的代码,其它的省略,   
  217.             //可见每个最小单元之后都可以是任意的空格分隔。               
  218.             if (t.indexOf(" ") == 0)continue;   
  219.                
  220.             foundToken = true;// 找到标识   
  221.   
  222.         } else {// 第二判断是不是以+~开头                 2   
  223.             re = /^([>+~])\s*(\w*)/i;   
  224.             if ((m = re.exec(t)) != null) {// 以+~开头的   
  225.                 r = [];   
  226.                 var merge = {};   
  227.                 nodeName = m[2].toUpperCase();// 节点名   
  228.                 m = m[1];// 符号,如+,~   
  229.                 // 如果selector字符串的匹配+或~(子元素),在结果集中所有元素中找到其兄弟元素节点。   
  230.                 //不同的+找的是后续的第一个兄弟元素,而~是找到所有的后续的兄弟元素节点。   
  231.                 //之后把找到的所有的元素放到结果集合中。   
  232.                 for (var j = 0, rl = ret.length;j < rl; j++) {   
  233.                     // 把~和+的操作统一在一起进行处理   
  234.                     var n = (m == "~" || m == "+"  
  235.                             ? ret[j].nextSibling: ret[j].firstChild);   
  236.                     for (;n; n = n.nextSibling)   
  237.                         if (n.nodeType == 1) {// 保证节点是元素类型   
  238.                             var id = jQuery.data(n);// 为n元素生成全局唯一的id   
  239.   
  240.                             if (m == "~" && merge[id])// 保证ret中元素不重复   
  241.                                 break;// nextSibling会循环到第一个节点?   
  242.                             if (!nodeName|| n.nodeName.toUpperCase() == nodeName) {   
  243.                                 if (m == "~")// 找到元素保存起来   
  244.                                     merge[id] = true;   
  245.                                 r.push(n);   
  246.                             }   
  247.                             if (m == "+")// 直接后续兄弟节点,只进行一次操作。   
  248.                                 break;   
  249.                         }   
  250.                 }   
  251.   
  252.                 ret = r;// 找到的所有的元素放到结果集合中。   
  253.                 t = jQuery.trim(t.replace(re, ""));   
  254.                 foundToken = true;   
  255.             }   
  256.         }   
  257.           
  258.         //// 不是以>~+开头的或者说除去已经分析过的字符,接下来的字符是不是>~+开头   
  259.         if (t && !foundToken) {   
  260.             //这里的意思是在开始的位置找到,号,说明一个selector已经完成了,那么   
  261.             //结果集就要存到已经完成的集合中。结果集也应该初如化。   
  262.             if (!t.indexOf(",")) {                  
  263.                 //说明运行到这里的时候,还是单个selector的字符串分析是刚刚开始   
  264.                 //因为>~+不可能得到ret[0]元素等于元素的自身。如果等于的话,   
  265.                 //那就清除出ret,因为接下来就要把ret结果集中的元素存入done中   
  266.                 if (context == ret[0])   
  267.                     ret.shift();   
  268.                        
  269.                 done = jQuery.merge(done, ret);// ret的其它元素放入done   
  270.                 r = ret = [context];// 重新初始化   
  271.                 //把,采用空格代替。   
  272.                 t = " " + t.substr(1, t.length);   
  273.             } else {   
  274.                 // 说明这一个selector部分还没有完成,同时还没有找到元素   
  275.                  // 或者是 >F的后面用空格来分隔。                
  276.                  //* qId:^((?:[\w*_-]|\.)+)(#)((?:[\w*_-]|\.)+)   
  277.                 // * qclass:^([#.]?)((?:[\\w*_-]|\.)*)                 
  278.                 var re2 = quickID;// 如(.)nodeName#idName   
  279.                 var m = re2.exec(t);// 找到第一个相配的   
  280.   
  281.                 if (m) {   
  282.                     m = [0, m[2], m[3], m[1]];// m=[0,#,idName,nodeName]   
  283.                 } else {   
  284.                     re2 = quickClass;// #nodeName,.className   
  285.                     m = re2.exec(t);// m=[all,#,idName]   
  286.                 }   
  287.   
  288.                 m[2] = m[2].replace(/\\/g, "");// 去除转义字符   
  289.                    
  290.                 //取数组的最后一个元素,其实就是为了取到是不是为document,   
  291.                 //因为只有document才有getElementById,为什么不直接采用   
  292.                 //document呢?难道document的查找会比先根据element.   
  293.                 //getElementsByTagName找到元素下面的tagname的相符的   
  294.                 //集合然后采用id属性去判断对比来得更慢吗?不一定?对于大的Dom树,   
  295.                 //而element的范围又很小的话,可能会慢一些。   
  296.                 //不过由于这里还要通过属性选择器来进行验证进行验证,一般来说    
  297.                 //element.getElementsByTagName会快一点。   
  298.                 var elem = ret[ret.length - 1];            
  299.                 if (m[1] == "#" && elem && elem.getElementById   
  300.                         && !jQuery.isXMLDoc(elem)) {   
  301.                     var oid = elem.getElementById(m[2]);   
  302.   
  303.                     // 回测元素的ID的确存在,在IE中会取name属性的值,同时在form 元素中   
  304.                     // 会选中在form中元素的name属性为id的元素。   
  305.                     if ((jQuery.browser.msie || jQuery.browser.opera) && oid   
  306.                             && typeof oid.id == "string" && oid.id != m[2])   
  307.                         //通过属性选择器来进行验证。    
  308.                         oid = jQuery('[@id="' + m[2] + '"]', elem)[0];   
  309.   
  310.                     // 回测元素的node Name是否相同,如div#foo,可以提交效率   
  311.                     ret = r = oid && (!m[3] || jQuery.nodeName(oid, m[3]))   
  312.                             ? [oid] : [];   
  313.                 } else {   
  314.                     //这里处理了#id,.class tagName,div#id四种形式   
  315.                     //这里我们还可以看出E F形式。并没有特殊的处理。就是采用了   
  316.                     //E.getElementsByTagName(F)就可以了。   
  317.                     //这个就能取后元素的所有后代为F和F的元素节点。   
  318.                     //和F使用是统一的起来。因为E都是结果集。   
  319.                     for (var i = 0;ret[i]; i++) {   
  320.                         //因为m有两种情况:[0,#,idName,nodeName]、[all,#/.,idName/class/tagName]   
  321.                         //这里就是根据这两种情况来取得tagName,m[1] == "#" && m[3]   
  322.                         //为真,说明是第一种,取nodeName。如果m[1] == "#" && m[3]为假   
  323.                         //说明m[1] <> "#"||!m[3],而m[1] != ""说明只能是第二个数组中的.或#   
  324.                         //说明了对于#nodeName,.className采用element.getElementsByTagName(*).   
  325.                         //当m[1] == "",说明是一个E 元素选择器,它不带任何的前缀。如:p。这个时候   
  326.                         //m[2]是tagName.   
  327.                         //m[0] == "" ,只能指第二个数组中的。它=="",说明没有找到符合qclass的regExp.   
  328.                         //其它的情况都不会为"",它为"",!m[1],!m[2]也都为true.                         
  329.                         var tag = (m[1] == "#" && m[3] ? m[3] : (m[1] != ""  
  330.                                 || m[0] == "" ? "*" : m[2]));// 分情况取tagName   
  331.                           
  332.                         //*的情况下,对于object标签转换为param标签进行操作。   
  333.                         if (tag == "*"&& ret[i].nodeName.toLowerCase() == "object")   
  334.                             tag = "param";// Handle IE7 being really dumb about <object>s   
  335.                                
  336.                         //把结果集合中第一个元素的getElementsByTagName存入到临时的结果集中。   
  337.                         r = jQuery.merge(r, ret[i].getElementsByTagName(tag));   
  338.                     }   
  339.                        
  340.                     //class选择器的话,就根据class属性在找到结果集合中过滤   
  341.                     if (m[1] == ".")    
  342.                         r = jQuery.classFilter(r, m[2]);   
  343.                         
  344.                     //id选择器的话,就根据id属性在找到结果集合中过滤   
  345.                     if (m[1] == "#") {   
  346.                         var tmp = [];   
  347.                         for (var i = 0;r[i]; i++)   
  348.                             if (r[i].getAttribute("id") == m[2]) {   
  349.                                 tmp = [r[i]];   
  350.                                 break;   
  351.                             }   
  352.   
  353.                         r = tmp;   
  354.                     }   
  355.   
  356.                     ret = r;   
  357.                 }   
  358.   
  359.                 t = t.replace(re2, "");   
  360.             }   
  361.   
  362.         }   
  363.   
  364.         //这个时候已经找到结果的集合,对于如CSS Selector为:hidden的属性筛选器,   
  365.         //它的集合就是context的下面的所有元素节点。也就是说上面的   
  366.         //代码无论如何都能找到元素的集合。这个集合可能是>/+~ F   
  367.         //或#id,.class tagName,div#id,对于不满足这些条件的,就采用   
  368.         //context.getElementsByTagName(*)要取得其下所有的元素   
  369.         //确保接下来的过滤(筛选)   
  370.         if (t) {// 根据余下的selector,对找到的r集合中的元素进行过滤   
  371.             var val = jQuery.filter(t, r);   
  372.             ret = r = val.r;   
  373.             t = jQuery.trim(val.t);// 去首尾空格   
  374.         }   
  375.     }   
  376.       
  377.     //如果还会有t存在说明一个问题:last == t   
  378.     //也就是上一次的过程中没有进行成功的解析一个最小单元的选择器   
  379.     //原因是输入的 t 字符串有语法上的错误。如果是采用,分隔的多选择器   
  380.     //那么就是当前及之后的选择器不分析。完成的done就是之前的结果集。   
  381.     //觉得这样处理不好,很多时间我们都会写错CSS selectror,不报错,   
  382.     //对于调试发现问题特难。      
  383.     if (t)  ret = [];   
  384.   
  385.     //出现这种情况说明运行到这里的时候,还是单个selector的字符串分析是刚刚开始   
  386.     //如果等于的话,那就清除出ret,因为接下来就要把ret结果集中的元素存入done中   
  387.     if (ret && context == ret[0])   
  388.         ret.shift();// 去掉根上下文   
  389.   
  390.     done = jQuery.merge(done, ret);// 合并   
  391.   
  392.     return done;   
  393. },   
  394. // 找到r中element中的className中含有m 或不含有的所有的元素   
  395. classFilter : function(r, m, not) {   
  396.     m = " " + m + " ";   
  397.     var tmp = [];   
  398.     for (var i = 0;r[i]; i++) {   
  399.         var pass = (" " + r[i].className + " ").indexOf(m) >= 0;   
  400.         if (!not && pass || not && !pass)   
  401.             tmp.push(r[i]);   
  402.     }   
  403.     return tmp;   
  404. },   
  405.   
  406. //根据CSS selector表达式查找集合中满足该表达式的所有元素   
  407. //还可以根据not来指定不满足CSS selector表达式元素集   
  408. filter : function(t, r, not) {   
  409.     var last;   
  410.   
  411.     while (t && t != last) {// t存在,且改变   
  412.         last = t;   
  413.         // Match: [@value='test'], [@foo]   
  414.         // 1、^(\[) *@?([\w:-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,   
  415.   
  416.         // Match: :contains('foo')   
  417.         // 2、^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,   
  418.   
  419.         // Match: :even, :last-child, #id, .class   
  420.         // 3、new RegExp("^([:.#]*)(" + chars + "+)")],         
  421.            
  422.         //这里可以看出我们直接调用filter的时候的selector如不是筛选器的话,   
  423.         //那就不进行筛选了,这里的selector语法如[@value='test'], [@foo]、   
  424.         //:contains('foo'),:even, :last-child, #id, .class的形式   
  425.         //可以是上面这几种形式的组合,但不能包括元素选择器。   
  426.         //而且复合的形式中间不能采用空格隔开,如[@value='test']#id.class可行的。   
  427.         var p = jQuery.parse, m;   
  428.         for (var i = 0;p[i]; i++) {// 找到与jQuery.parse中regexp相配的   
  429.             m = p[i].exec(t);   
  430.             if (m) {   
  431.                 t = t.substring(m[0].length);//删除处理过的字符部分   
  432.                 m[2] = m[2].replace(/\\/g, "");// 有可能会有没有转换的\去掉   
  433.                 break;   
  434.             }   
  435.         }           
  436.         // 与上面三种的regexp都不相配   
  437.         if (!m) break;   
  438.   
  439.         //处理 :not(.class)的形式,返回集合中不包含.class的其它的元素   
  440.         if (m[1] == ":" && m[2] == "not")   
  441.             // 性能上优化 m[3]是.class经常出现   
  442.             r = isSimple.test(m[3])// isSimple = /^.[^:#\[\.]*$/   
  443.                     ? jQuery.filter(m[3], r, true).r: jQuery(r).not(m[3]);   
  444.                        
  445.         //处理.class的过滤。只要看看m[2]这个class是不是被集合中元素的class包含。   
  446.         else if (m[1] == ".")// 性能上优化考虑   
  447.             r = jQuery.classFilter(r, m[2], not);   
  448.             //处理属性过滤。如[@value='test']形式的属性选择   
  449.         else if (m[1] == "[") {   
  450.             var tmp = [], type = m[3];// 符号,如=   
  451.   
  452.             for (var i = 0, rl = r.length;i < rl; i++) {   
  453.                 //jQuery.props[m[2]]进行tag中属性名和对应的元素的属性名转换,   
  454.                 //因为tag中属性名是元素中简写,z取到 元素的属性值   
  455.                 var a = r[i], z = a[jQuery.props[m[2]] || m[2]];   
  456.                    
  457.                 //直接取元素的属性值,没有取到,说明有的浏览器不支持这种方法   
  458.                 //进一步尝试采用jQuery.attr来进行非标准的兼容取属性值。   
  459.                 //就算是取到了值,但对于属性名为style|href|src|selected,   
  460.                 //它们不能直接取值,要进行特殊的处理,这个在jQuery.attr进行。   
  461.                 //其实这里可以直接采用jQuery.attr(a, m[2]),一步到位。   
  462.                 if (z == null || /style|href|src|selected/.test(m[2]))   
  463.                     z = jQuery.attr(a, m[2]) || '';// 几个特殊的处理   
  464.   
  465.                 //如果属性选择器满足这[foo],[foo=aa][foo!=aa][foo^=aa][foo$=aa][foo~=aa]   
  466.                 //这几种方式之一,这个元素就可能通过。即满足条件。m[5]属性值。   
  467.                 if (   (type == "" && !!z//[foo]   
  468.                         || type == "=" && z == m[5]//[foo=aa]   
  469.                         || type == "!=" && z != m[5]//[foo!=aa]   
  470.                         || type == "^=" && z&& !z.indexOf(m[5])//[foo^=aa]   
  471.                 

抱歉!评论已关闭.