Ruby's Louvre

每天学习一点点算法

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

mass.query v2 发布

我的选择器第二版发布,全面支持jQuery所支持的所有伪类(第一版就支持除has伪类的所有伪类,换言之,这版只增加了对它的支持)。代码量减少了100多行,搜索速度平均提升200ms。在IE6下为jQuery1.4.4的2倍多,IE8,FF3.6下由于大家都使用querySelectorAll,快不了多少,但也比jQuery快。支持HTML与XML。

query v2使用了全新的选择组群组切割技术,这是它更快的主要原因。这里解释一下,什么叫选择器群组。一般而言,我们想搜索一个或几个目标元素,需要传入一个CSS字符串,这个字符串有时很长。比如这个——“div.aaa , p:not(#aaa.bb)”,它其实是由几个选择器组成,依次是标签选择器div, 类选择器.aaa,并联选择器“,”,标签选择器p,反选选择器:not(#aaa.bb),注意伪类选择器括号里面的表达式有时也是一个选择器群组,比如这次的ID选择器#aaa,类选择器.bb。没有querySelectorAll或选择器组群出现自定义伪类的情况下,我们对于这长长的字符串进行切割,才能搜索到我们的结果。因此,要完成一个javascript选择器,无疑要拥有强大的切割技术,去掉不必要的空白与模板,分割成浏览器能辨识的ID,标签名,类名等等。

那怎么切割它呢?!最佳选择无疑就是正则。正则就是为处理字符串而生。我们打开Sizzle,Sly,mini,Slick这些著名选择器时,都会看到一大串令人印象深刻的正则,虽然你一时半刻是看不透它是干什么的。但CSS选择器的类型是如此的多,算上最新出现的命名空间选择器,一共有12种选择器(ID,类,标签,通配符,属性,并联,后代,亲子,相邻,兄长,伪类,命名空间),要一次性切割它们,这需要何等的技术啊。就算能,这正则都长得可怕。我的选择器第一版由于在这方面的短板,被逼取巧地走另一条路,通过对特殊元字符的判定,调用相应的正则来实现切割,切割之后再切割,一直切到可用的部分。听起来很神奇,但这是个笨方法,让我在选择器失速不少!历经差不多一年时间,我对它做不少改进,但整体结构不变,也提升不了什么速度。这次升级,我搞了一个函数,终于完成这变革。

下面是我的选择器转数组函数(与测试代码混在一起,测试代码用到jQuery),一次性把选择器切割为最小颗粒。

   $(function(){
   var reg_split = /(?:\(.*\)|[^,#:\.\s+>~[\](])+|[\.\[\]#:+>~,]|\s+/g
//   var reg_pseudo = /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/;
   var reg_pseudo = /^:(\w[-\w]*)(\((.+)\))?$/
   var one_identify  =  {".":1,"#":1,":":1}  //dom.oneObject(".#[:".split(''));
   var arrayize = function(sss){
     sss = sss.match(reg_split);
     var result = [],match,next,prev
     for(var s=0,ss;ss = sss[s++];){
       next = sss[s],prev = sss[s-2]
       if(ss === ":" && next && next.indexOf("not") ===0 && (match = reg_pseudo.exec(ss+next))){
         //如果是反选选择器,则将其里面的表达式取出来,转换为数组插入到选择器群里面进行匹配,
         //并且让其后面紧跟着一个自定义的“反反选选择器”来标识反选选择器的结束
         //注:反选选择器的括号里可以含有以下选择器:id,class,attribute,pseudo(not除外)
         result = result.concat(":not").concat(arrayize(match[3])).concat(":yes");
         s++;
       }else if(ss === "[" ){//处理属性选择器
               while(next!=="]"){
                   ss += next;
                   next = sss[++s];
               }
               ss += next
               result.push(ss)
               s++
       }else if(one_identify[ss] && next){
         result.push(ss+next);//合并id,class,pseudo选择器的界定符与它的主体部分
         s++
       }else if(/^\s*$/.test(ss)){//如果空白位于并联选择器的左右或整个群组的两端,忽略掉,否则重写后代选择器为!
         if(next ==="," || next ===void 0 || prev === "," || prev === void 0)
           continue
         result.push("!");
       }else{
         result.push(ss);
       }
     }
     return result;
   }
   $("#split_button").click(function(){
     var selectors = $("#split_target").val();
     var array = adjuest(selectors)
     $("#split_result").text(array.length+"☆"+array.join("★"));
   });
 })

一些测试选择器群组:

  • :not(#aaa.bb)
  • input[id][name='man'][ee!=555]
  • div:has(p)
  • div~div div

源码(如果发现有什么改进之处,欢迎提出。)

selectors DOMAssistant2.8 query v2 jQuery1.43 Sly inQuery EXT 2.2 MooTools 1.3
IE8 643 309 294 364 982 426 2433
IE7 2052 1148 2354.75 1651 2859 969 5936
IE6 1156 710 1431 997 1677 594 3528
opera10.60 24 12 26 21 52 58 153
firefox 3.6.12 46 44 52 71 152 114 107

下载:点我 解压后有一个叫frameworks的文件夹,里面的query.js与queryv2就是我的选择器。 这里特别感谢javaeye的achun,是他把mootools的selector测试工具slickspeed改成纯JS版的。 给大家两幅直观的图片(运行环境为IE6,绿色快,灰色一般,红色慢,黑色报错不支持)

如果您觉得此文有帮助,可以打赏点钱给我支付宝1669866773@qq.com ,或扫描二维码

posted on   司徒正美  阅读(2860)  评论(8编辑  收藏  举报

编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
历史上的今天:
2009-11-25 IE6的getElementById bug
点击右上角即可分享
微信分享提示