Sizzle一步步实现所有功能(层级选择)

第二步:实现Sizzle("el,el,el..."),Sizzle("el > el"),Sizzle("el el"),Sizzle("el + el"),Sizzle("el ~ el")

  1 (function( window ){
  2
  3 var arr = [];
  4 var select ;
  5 var Expr;
  6 var push = arr.push;
  7 // http://www.w3.org/TR/css3-selectors/#whitespace
  8 // 各种空白待穿正则字符串
  9 var whitespace = "[\\x20\\t\\r\\n\\f]";
 10 // 带空格选择器正则,记忆无空格选择器
 11 // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
 12 var    identifier = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+";
 13 // 属性选择器: http://www.w3.org/TR/selectors/#attribute-selectors
 14 var    attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace +
 15         // Operator (capture 2)
 16         "*([*^$|!~]?=)" + whitespace +
 17         // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
 18         "*(?:‘((?:\\\\.|[^\\\\‘])*)‘|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace +
 19         "*\\]";
 20 var rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" );
 21 // 快速选择器正则 ID 或者 TAG(包括*) 或者 CLASS 选择器
 22 var rquickExpr = /^(?:#([\w-]+)|(\w+|\*)|\.([\w-]+))$/;
 23 // 连接符号
 24 var rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" );
 25 // 层级符号正则‘>‘,‘ ‘,‘+‘,‘~‘
 26 var rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" );
 27 var matchExpr = {
 28         "ID": new RegExp( "^#(" + identifier + ")" ),
 29         "CLASS": new RegExp( "^\\.(" + identifier + ")" ),
 30         "TAG": new RegExp( "^(" + identifier + "|[*])" ),
 31 };
 32 // 浏览器代码正则
 33 var rnative = /^[^{]+\{\s*\[native \w/;
 34 // token缓存
 35 var tokenCache = createCache();
 36 // 编译缓存
 37 var compilerCache = createCache();
 38 // 入口
 39 function Sizzle( selector ){
 40     // 清除空格
 41     selector = selector.replace( rtrim, "$1" )
 42     var results = [];
 43     var match;
 44     var matcher;
 45     var elem;
 46     var m;
 47     var context = document;
 48
 49     // 是否为最简选择器
 50     if( match = rquickExpr.exec( selector )){
 51         // Sizzle(‘#ID)
 52         if ( (m = match[1]) ) {
 53             elem = context.getElementById( m );
 54             if( elem ){
 55                 results.push( elem );
 56             }
 57             return results;
 58
 59         // Sizzle("TAG")
 60         }else if( (m = match[2]) ){
 61             push.apply( results, context.getElementsByTagName( selector ) );
 62             return results;
 63
 64         // Sizzle(".CLASS")
 65         }else if( (m = match[3]) ){
 66             // 支持getElementsByClassName
 67             if( support.getElementsByClassName ){
 68                 push.apply( results, context.getElementsByClassName( m ) );
 69                 return results;
 70             }
 71         }
 72     }
 73     // 复杂选择调到select
 74     return select( selector, context, results);
 75 }
 76 // 创建缓存函数
 77 function createCache() {
 78     var keys = [];
 79
 80     function cache( key, value ) {
 81         // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
 82         if ( keys.push( key + " " ) > 10 ) {
 83             // Only keep the most recent entries
 84             delete cache[ keys.shift() ];
 85         }
 86         return (cache[ key + " " ] = value);
 87     }
 88     return cache;
 89 }
 90 // 错误函数
 91 Sizzle.error = function( msg ) {
 92     throw new Error( "Syntax error, unrecognized expression: " + msg );
 93 };
 94 // 版本支持变量的对外访问入口
 95 var support = Sizzle.support = {};
 96
 97 // 判断是否支持getElementsByClassName
 98 // 支持: IE<9
 99 support.getElementsByClassName = rnative.test( document.getElementsByClassName );
100 // 表达式对象
101 // 存放各类相对位置,各种查询函数,各种过滤函数等。
102 Expr = {
103     relative: {
104         ">": { dir: "parentNode", first: true },
105         " ": { dir: "parentNode" },
106         "+": { dir: "previousSibling", first: true },
107         "~": { dir: "previousSibling" }
108     },
109     filter: {
110         "TAG": function( nodeNameSelector ) {
111             var nodeName = nodeNameSelector.toLowerCase();
112             return nodeNameSelector === "*" ?
113                 function() { return true; } :
114                 function( elem ) {
115                     return elem.nodeName.toLowerCase() === nodeName;
116                 };
117         },
118         "CLASS": function( className ) {
119             var className = className.toLowerCase();
120             return function( elem ) {
121                     return elem.className.toLowerCase() === className;
122             };
123         }
124     },
125     find: {
126         "TAG": function( tag, context ) {
127             return context.getElementsByTagName( tag );
128         },
129         "CLASS": support.getElementsByClassName&&function( tag, context ) {
130             return context.getElementsByClassName( tag );
131         },
132     },
133 }
134 // tokenize函数
135 // 将选择器字符串转化为方便使用的数组对象形式
136 tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
137     var cached = tokenCache[ selector + " " ];
138
139     // cached.slice生成新的数组,对其修改不会修改其引用缓存
140     if ( cached ) {
141         return cached.slice( 0 );
142     }
143     // 循环条件
144     var soFar = selector;
145     // 结果数组
146     var groups = [];
147     // 匹配参数
148     var matched;
149     // 一个独立的tokens
150     var tokens;
151     // 辅助变量
152     var match;
153
154     while ( soFar ) {
155
156         //首次默认创建一个tokens
157         //之后每碰到一个逗号新增一个新的tokens
158         if ( !matched || (match = rcomma.exec( soFar )) ) {
159             if ( match ) {
160                 // Don‘t consume trailing commas as valid
161                 soFar = soFar.slice( match[0].length ) || soFar;
162             }
163             groups.push( (tokens = []) );
164         }
165
166         matched = false;
167
168         // 关系token
169         if ( (match = rcombinators.exec( soFar )) ) {
170             matched = match.shift();
171             tokens.push({
172                 value: matched,
173                 // Cast descendant combinators to space
174                 type: match[0].replace( rtrim, " " )
175             });
176             soFar = soFar.slice( matched.length );
177         }
178
179         // TAG,CLASS,ID token
180         for ( type in Expr.filter ) {
181             if ( match = matchExpr[ type ].exec( soFar ) ) {
182                 matched = match.shift();
183                 tokens.push({
184                     value: matched,
185                     type: type,
186                     matches: match
187                 });
188                 soFar = soFar.slice( matched.length );
189             }
190         }
191         // 一次循环到这里三个条件都不符合没有匹配结果时,跳出。
192         if ( !matched ) {
193             break;
194         }
195     }
196
197     // 意外跳出,soFar存在,报错。
198     return soFar ?
199             Sizzle.error( selector ) :
200             // 缓存后转成新数组返回(预防修改缓存内容)
201             tokenCache( selector, groups ).slice( 0 );
202 };
203 // 将tokens转化为selector字符串形式。
204 function toSelector( tokens ) {
205     var i = 0,
206         len = tokens.length,
207         selector = "";
208     for ( ; i < len; i++ ) {
209         selector += tokens[i].value;
210     }
211     return selector;
212 }
213 // !addCombinator
214 // 增加关系处理函数
215 // 返回关系函数,主要功能是,遍历种子节点的关系节点。
216 // 比如li>a,传入无数个种子节点a,a.parentNode,再执行matcher,matcher里再判断这个父亲节点是不是li
217 function addCombinator( matcher, combinator ) {
218     var dir = combinator.dir;
219     return combinator.first ?
220         function( elem, context ) {
221             while( (elem = elem[ dir ]) ){
222                 if ( elem.nodeType === 1 ) {
223                     return matcher( elem, context );
224                 }
225             }
226         }:
227         function( elem, context ) {
228             while ( (elem = elem[ dir ]) ) {
229                 if ( elem.nodeType === 1 ) {
230                     if(matcher( elem, context )) {
231                         return true;
232                     }
233                 }
234             }
235             return false;
236         }
237 }
238
239 // !elementMatcher
240 // 生成matchers遍历器
241 // matchers数组存放我要过滤的函数,这个函数遍历所有过滤函数,一个不符合就返回false。
242 function elementMatcher( matchers ) {
243     return function( elem, context ) {
244         var i = matchers.length;
245         while ( i-- ) {
246             if ( !matchers[i]( elem, context ) ) {
247                 return false;
248             }
249         }
250         return true;
251     };
252 }
253 // !matcherFromTokens
254 // 根据tokens,生成过滤一组函数matchers,供elementMatcher使用
255 // 返回的是一个执行所有过滤函数的函数
256 function matcherFromTokens( tokens ){
257     var matchers = [];
258     var matcher;
259     var i = 0;
260     var len = tokens.length;
261     for ( ; i < len; i++ ) {
262         if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
263             matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
264         } else {
265             matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
266             matchers.push( matcher );
267         }
268     }
269     return elementMatcher( matchers );
270 }
271 // !matcherFromGroupMatchers
272 // 返回超级匹配器,
273 function matcherFromGroupMatchers( elementMatchers ){
274     // !!最重要superMatcher,也是最核心的函数,其它的函数为它服务。
275     // 获取种子元素,遍历所有种子元素。
276     // 遍历elementMatchers
277     // 符合的推入结果数组
278     // 一个选择器(逗号隔开的)生成一个elementMatcher,elementMatchers是存放所有elementMatcher的数组
279     var superMatcher = function( seed, context, results) {
280         var elems = seed || Expr.find["TAG"]( "*", document );
281         var len = elems.length;
282         var i = 0;
283         for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
284             j = 0;
285             while ( (matcher = elementMatchers[j++]) ) {
286                 if ( matcher( elem, context) ) {
287                     results.push( elem );
288                     break;
289                 }
290             }
291         }
292     }
293     return superMatcher;
294 }
295 // compile
296 // 最初的编译器,存放elementMatchers,缓存超级匹配函数并返回
297 compile = Sizzle.compile = function( selector, match ) {
298     var i;
299     var elementMatchers = [];
300     var    cached = compilerCache[ selector + " "];
301     if ( !cached ) {
302         i = match.length;
303         while ( i-- ) {
304             elementMatchers.push( matcherFromTokens( match[i] ) );
305         }
306         cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers ));
307     }
308     return cached;
309 }
310 // select
311 // 兼容的自写的选择器
312 select = Sizzle.select = function( selector, context, results){
313     var token;
314     var seed;
315     var tokens;
316     var find;
317     var match = tokenize( selector )
318     if ( match.length === 1 ) {
319         // tokens
320         var tokens = match[0].slice( 0 );
321         // 如果tokens的首项是ID,将其设置为上下文
322         if ( (token = tokens[0]).type === ‘ID‘ ){
323             context = document.getElementById(token.matches[0]);
324             selector = selector.slice( tokens.shift().value.length );
325         }
326         // 生成种子seed
327         // 如"div ul li",所谓种子就是所有的li
328         // 后面编译函数需要过滤出符合祖先是ul,ul的祖先是div的节点
329         i = tokens.length;
330         while ( i-- ){
331             token = tokens[i];
332             if ( Expr.relative[ (type = token.type) ] ) {
333                 break;
334             }
335             if((find =  Expr.find[ type ]))
336                 if( seed = find( token.matches[0],context ) ) {
337                     tokens.splice( i, 1 );
338                     selector = toSelector( tokens )
339                     break;
340                 }
341         }
342     };
343     // 根据selector,tokens(match防止对原tokens修改)生成superMatcher并调用
344     compile( selector, match )( seed, context, results );
345     return results;
346 }
347
348 // 对外入口
349 window.MSizzle = Sizzle;
350
351 })(window)
352 // 测试
353 console.log(MSizzle("ul.topnav > li"))
354 console.log(MSizzle("ul.topnav   li"))
355 console.log(MSizzle("ul.topnav + div"))
356 console.log(MSizzle("ul.topnav ~ div"))

1.

时间: 2024-10-27 13:48:25

Sizzle一步步实现所有功能(层级选择)的相关文章

Sizzle一步步实现所有功能(一)

前提: 1.HTML5自带querySelectAll可以完全替代Sizlle,所以我们下面写的Sizzle,是不考虑QSA的. 2.作者考虑了大量兼容情况,比如黑莓4.6系统这样几乎接触不到的bug.这样学习价值不高却很费时间问题我不去考虑.主要考虑IE8,这也是Sizzle没被淘汰的最主要原因. 3.我喜欢采用var 声明每个变量,而不是一个var 声明好多变量.原因是我在一步步完善模仿的Sizzle,会有大量的修改. 4.Sizzle的原理实际很简单,真的就可以这样一句话遍历页面所有元素,

文件夹的层级选择&lt; OC实现 &gt;

类似文件夹的层级选择,可以搜索和创建新文件夹,点击路径标题可以返回对应层级. 界面有点丑,功能还是大概实现了的!! 代码有点多,还是附上地址吧,有兴趣的可以看看哟!!!    https://github.com/PengSiSi/ResourceSelectDemo

Android 实现用户列表信息滑动删除功能和选择删除功能

在项目开发过程中,常常需要对用户列表的信息进行删除的操作.Android中常用的删除操作方式有两种 ,一种就是类似微信的滑动出现删除按钮方式,还有一种是通过CheckBox进行选择,然后通过按钮进行删除的方式.本来的实例集成上述的两种操作方式来实现用户列表删除的效果. 设计思路:在适配器类MyAdapter一个滑动删除按钮显示或隐藏的Map,一个用于CheckBox是否选中的Map和一个与MainAcitivyt进行数据交互的接口ContentsDeleteListener,同时该接口包含两个方

crm功能及选择crm注意事项

在这个互联网信息技术快速发展的时代,越来越多的企业也开始引入了CRM系统来助力于企业的客户管理.不过,有些企业不是很了解其涉及到的管理功能有哪些. 一.CRM系统涉及到了哪些管理功能呢?CRM系统,也就是客户关系管理系统,是指企业用CRM系统来管理与客户之间的关系.通常所指的CRM系统是指用计算机自动化分析销售.市场营销.客户服务以及应用等流程的软件系统. 它的目标是通过提高客户的价值.满意度.盈利性和忠实度来缩减销售周期和销售成本.增加收入.寻找扩展业务所需的新的市场和渠道. CRM系统设计范

ps aux 和ps -aux和 ps -ef的功能及选择

Linux中的ps命令是Process Status的缩写.ps命令用来列出系统中当前运行的那些进程.ps命令列出的是当前那些进程的快照,就是执行ps命令的那个时刻的那些进程,如果想要动态的显示进程信息,就可以使用top命令. 要对进程进行监测和控制,首先必须要了解当前进程的情况,也就是需要查看当前进程,而 ps 命令就是最基本同时也是非常强大的进程查看命令.使用该命令可以确定有哪些进程正在运行和运行的状态.进程是否结束.进程有没有僵死.哪些进程占用了过多的资源等等.总之大部分信息都是可以通过执

Android 跳转系统选择本地视频的功能

今天在项目开发的过程中产品要求添加选择本地视频的功能,于是就翻阅和查找各种资料,进行功能的开发,但是在开发过程中发现,各种不同的品牌的手机跳转至系统选择本地视频的功能结果不太一样,所以我就对一些主流的品牌进行了测试,现做如下总结: 1.选择本地视频的功能 Button click event: Intent intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Video.Media.EXTERNAL_CONTEN

Mac预览怎么用 Mac预览功能实用技巧大全

Mac预览怎么用?有很多的用户以为Mac自带的预览功能只具有简单的图片浏览功能,其实不然,其实"预览"是一款强大的看图.修图.改图软件,也同时是一款多功能的 PDF 阅读与编辑工具,接下来Pc6苹果小编给大家详细介绍一下Mac预览功能实用技巧. 目前的 Mac 预览程式可以支持 TIFF. PNG. JPEG. GIF. BMP. PDF 等主要文件格式的浏览与编辑,而单纯预览上也可以支持像是 Photoshop 等专业图像处理软件. Office 等专业文书处理软件所产生的特殊文件格

C#开发微信门户及应用(25)-微信企业号的客户端管理功能

我们知道,微信公众号和企业号都提供了一个官方的Web后台,方便我们对微信账号的配置,以及相关数据的管理功能,对于微信企业号来说,有通讯录中的组织架构管理.标签管理.人员管理.以及消息的发送等功能,其中微信企业号的组织架构和标签可以添加相应的人员,消息发送可以包含文本.图片.语音.视频.图文.文件等内容.对于企业号来说,官方的接口几乎可以无限的发送消息,因此构建一个管理后台,管理企业号的人员,以及用来给企业成员发送消息就是一个很好的功能亮点,有时候可以提高我们企业内部的消息通讯效率和日常工作管理效

如何选择项目管理工具?

本文和大家分享的主要是项目管理工具的选择相关内容,作为 产品经理,我们对项目管理工具的使用是必不可少的,那我们如何选择项目管理工具呢?一起来看看吧. 创业团队不同于大公司,它具有扁平.高效的特点.这些特点能帮助它快速决策,立即执行,在有限的时间内完成超量的工作.但是要保证这些特点能够持续地发挥作用,我们不仅要运用一些方法,同时在工具的选择上也要符合具体的场景与团队所拥有的配置.力求把特点发挥的作用最大化. 为了提高项目管理水平,赢得市场竞争,项目管理软件热度很高,在这里我分享一些小技能,避免选择