Sizzle揭秘—Sizzle选择器引擎的入口
前面说了Sizzle的两大利器,今天核心Sizzle结构
显示行数的有点问题,因为代码太长是分了两次,后来拼起来的见谅!
1: //Sizzle选择器的入口2: var Sizzle = function(selector, context, results, seed) {3: //初始化各个数据结果集和初始上下文4: results = results || [];5: var origContext = context = context || document;6: //如果初始的上下文即不是元素节点或是document节点则返回空的结果集7: if ( context.nodeType !== 1 && context.nodeType !== 9 ) {8: return [];9: }10: //如果选择字符串为空或selector不是string类型则直接返回结果集result 11: if ( !selector || typeof selector !== "string" ) {12: return results;13: }14: 15: var parts = [], m, set, checkSet, extra, prune = true, contextXML = isXML(context),16: soFar = selector;17: //chunker是Sizzle中一个重要的正则表达式 var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,18: //其实蛮长,引用一下别人的解释19: //第一部分:((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|20: [^[\]]+)+\]|\\.|[^ >+~, (\[]+)+|[>+~]] 21: 第二部分: (\s*,\s*)? 22: 第一部分可以以|(表示或的意思)为分界符做进一步的切分。则切分后的正则表达式如下所示 23: // //正则表达式起始符 24: // ( //第一个子表达式 25: // (?:\((?:\([^()]+\) //匹配伪类选择符 26: // |[^()]+)+\) //匹配函数式选择符 27: // |\[(?:\[[^[\]]*\] //匹配属性选择符 28: // |['"][^'"]*['"] //匹配属性选择符 29: // |[^[\]'"]+)+\] //匹配属性选择符 30: // |\\. //匹配Class选择符 31: // |[^ >+~,(\[\\]+)+ //匹配关系选择符 32: // |[>+~] //匹配关系选择符 33: // ) 34: // (\s*,\s*) //匹配选择符组中间的分隔符 35: // ? //非贪婪模式匹配 36: // / //正则表达式终止符 37: // g //正则表达式属性,全局匹配 38: // /* 39: //解释了这么多,就是将整个选择字符串按“+” “ ” “>” "~"分割,遇到","就停止匹配 40: //举个完整的例子就是"div ~ ul li[title='a'].sel , div ul li#mm" 这个选择字符串进过这次正则表达式处理后就会有parts=["div","~","ul"," ","li[title='a'].sel"] extra="div ul li#mm";41: // Reset the position of the chunker regexp (start from head)42: while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) {43: soFar = m[3];44: 45: parts.push( m[1] );46: //如果m[2]不为空则说明匹配了','并列连接符,则将‘,’后面的内容作为另外一次sizzle选择的选择字符串,后面会看到47: if ( m[2] ) {48: extra = m[3];24: break;25: }26: }//Sizzle对有伪位置选择符的选择字符串和没有处理是不一样的 origPos=/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/27: //Sizzle对有伪位置类选择器的选择字符串的处理和没有是不一样的。有的话会按照从左到右的方向进行解析,没有则按照Sizzle的标准解析从右往左进行解析28: if ( parts.length > 1 && origPOS.exec( selector ) ) {29: //如果选择字符串按照层级关系只有两部分,则合并这两项 举例 parts=[">","li.sel"] 会合并为"> li.sel".30: //relative同Expr.filter Expr.prefilters Expr.find 是作用其实都相仿。relative对象有"+" ">" "" "~"这四个属性,对不同的层级关系就行处理31: if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {32: set = posProcess( parts[0] + parts[1], context );33: } else {34: //如果第一个字符串是一个层级标识符,则已则以[context]作为结果集初始值 否则弹出第一个元素就行解析并将解析结果作为结果集初始值,如"ul > li.sel"则 调用Sizzle("ul",context),,所有ul DOM作为结果集初始值35: //其实这是一个递归调用,先不用关注具体的递归细节,我们先将所有的流程走下来36: set = Expr.relative[ parts[0] ] ?37: [ context ] :38: Sizzle( parts.shift(), context );39: //对parts中的元素就行遍历40: while ( parts.length ) {41: //对剩余字符串的解析基本上就是组合层级字符串和后面紧接的部分 举例来说假如经过上面的结果集初始化后 选择字符串还剩下parts=[">","div.mm","~","ul","li#gg"]42: //解析循环调用过程下 : set=posProcess(">div.mm",set);43: // set=posProcess("~ul",set)44: // set=posProcess("li#gg"); 45: // selector = parts.shift();46: 47: if ( Expr.relative[ selector ] ) {48: selector += parts.shift();49: }50: 51: 52: 53: //在posProcess是先将selector的伪位置类选择符剔除,然后递归调用Sizzle(“剔除后的字符换”,set),然后对Sizzle返回的结果进行伪位置类选择符过滤
//Sizzle("被剔除的伪位置选择符",上一步Sizzle返回的结果),这就完成了一个字符串的处理了
54: set = posProcess( selector, set );55: }56: 57: }58: //这部分不是jQuery的源码 是我在关于那篇remove参数中出现伪位置类选择符时出现非预期结果时,加上的测试代码59: //如果有兴趣可以在我的博客中找到那篇文章,如果没有兴趣直接跳过,没有任何影响。60: // if(parts.length==0&&seed){61: // var p_result=new Array();62: // var seeds=makeArray(seed);63: // var i;64: // for(i=0;i<seeds.length;i++)65: // { 66: // var j;67: // for(j=0;j<set.length;j++){68: // if(seeds[i]==set[j])69: // p_result.push(seeds[i]);70: // }71: // }72: // set=p_result;73: //74: //增加的代码至此结束 }75: } else {76: //如果没有伪位置类选择符则是另外一种处理方法77: // Take a shortcut and set the context if the root selector is an ID78: // (but not if it'll be faster if the inner selector is an ID)79: //上面有英文的注释,基本上就是做了一个小的优化,但这种优化仅在第一个分割字符串含有ID而最后一个分割字符串没有ID的情况下进行 这对性能的提高很有好处80: //因为ID是唯一的,如果最后一个分割字符串是具有ID则很容找出来,而在这种情况下就不会去做这种优化了81: if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&82: Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {83: var ret = Sizzle.find( parts.shift(), context, contextXML );84: //这里find函数和filter函数在前面是介绍过的find函数返回的ret中的expr是剩余字符串 如"ul.sel"则返回的结果是ret中set是多有class=sel的元素,expr中是ul,便于下一步进行过滤85: //filter过滤函数不像find函数值解析一部分,而是解析全部。如ul[title='a'].sel就会在一次过滤中全部处理完86: context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];87: }88: 89: if ( context ) {90: //如果seed种子集不为空,则让他作为初始结果集(可以对比一下有无伪位置类选择符处理可以发现有伪位置类选择符的处理是没有用到seed的)91: //如果不是则弹出最后的一个字符串调用find函数 92: //最后对字符串的剩余部分进行过滤93: var ret = seed ?94: { expr: parts.pop(), set: makeArray(seed) } :95: Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );96: set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;97: //如果此时parts中还有元素则将结果集转化为数组,如果没有元素就射prune=false ,如果原始的选择字符串没有层级关系就会出现这种情况,"li#gg"就是例子98: if ( parts.length > 0 ) {99: checkSet = makeArray(set);100: } else {101: prune = false;102: }103: //在前面的checkset的基础上进一步筛选元素104: while ( parts.length ) {105: var cur = parts.pop(), pop = cur;106: 107: if ( !Expr.relative[ cur ] ) {108: cur = "";109: } else {110: pop = parts.pop();111: }112: //此时pop代表层次过滤函数的参数,cur代表层级过滤类型(“+” “>" "" "~")113: 114: if ( pop == null ) {115: pop = context;116: }117: //调用对应的层级筛选函数 其实可能已经知道了relative内部也是通过调用Sizzle.filter进行过滤的118: Expr.relative[ cur ]( checkSet, pop, contextXML );119: }120: } else {121: checkSet = parts = [];122: }123: }124: //如果是没有层级关系的选择字符串会出现这样的情况 "li#gg"为例,参考上面关于此种情况的解释125: if ( !checkSet ) {126: checkSet = set;127: }128: //如果到这不仍然为空则选择字符串的编写是有问题的129: if ( !checkSet ) {130: Sizzle.error( cur || selector );131: }132: //call就是以另一种方式调用一个函数133: if ( toString.call(checkSet) === "[object Array]" ) {134: //prune是在上面判断的,如在解析没有层级关系的字符串时就会赋值为false,此时直接将结果赋值到result后面。135: if ( !prune ) {136: results.push.apply( results, checkSet );137: } else if ( context && context.nodeType === 1 ) {138: //我们知道在filter函数中inplace设为true是会修改CheckSet的,符合过滤条件的元素设为true,不符合设为false。那下面应该是很容易理解的了。set是最开始的结果集。139: for ( var i = 0; checkSet[i] != null; i++ ) {140: if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {141: results.push( set[i] );142: }143: }144: } else {145: //是其他情况则将Checkset中节点元素附加到结果集后面,146: for ( var i = 0; checkSet[i] != null; i++ ) {147: if ( checkSet[i] && checkSet[i].nodeType === 1 ) {148: results.push( set[i] );149: }150: }151: }152: } else {153: makeArray( checkSet, results );154: }155: //如果有并联连接字符串则递归处理后面由“,”分开的字符串156: if ( extra ) {157: Sizzle( extra, origContext, results, seed );158: Sizzle.uniqueSort( results );159: }160: //返回结果集161: return results;162: };
Sizzle揭秘—Sizzle选择器引擎的入口相关推荐
- mootools 选择器_MooTools中的Sizzle和Peppy选择器引擎
mootools 选择器 A few weeks back I touched on how you could implement the Peppy and Sizzle selector eng ...
- JQuery - Sizzle选择器引擎原理分析
说明:14年学习的jquery源码,搬到这里供大家交流.原文地址:https://segmentfault.com/a/1190000003933990 一.前言 Sizzle原来是jQuery里面的 ...
- jQuery选择器引擎和Sizzle介绍
jQuery选择器引擎和Sizzle介绍 首先介绍一下什么是Sizzle: Sizzle是一个纯javascript CSS选择器引擎.jquery1.3开始使用sizzle,Sizzle一反传统采取 ...
- jQuery中的选择器引擎Sizzle
读Sizzle的源码,分析的Sizzle版本号是2.3.3. Sizzle的Github主页 浏览器原生支持的元素查询方法: 方法名 方法描述 兼容性描述 getElementById 根据元素ID查 ...
- Sizzle选择器揭秘--Sizzle过滤器
上面一篇文章说的Sizzle的利器之一find函数Sizzle选择器揭秘--Sizzle选择器 这篇介绍Sizzle的另一利器filter函数 1: Sizzle.filter = function( ...
- 深入学习jquery源码之jQuery的选择器引擎Sizzle(一)
深入学习jquery源码之jQuery的选择器引擎Sizzle Sizzle是一个纯javascript CSS选择器引擎.jquery1.3开始使用sizzle,Sizzle一反传统采取了相反的Ri ...
- jquery选择器引擎Sizzle
首先介绍一下什么是Sizzle: Sizzle是一个纯JavaScript CSS选择器引擎.jquery1.3开始使用sizzle,Sizzle一反传统采取了相反的Right To Left的查询匹 ...
- jQuery中的选择器引擎Sizzle 1
读Sizzle的源码,分析的Sizzle版本号是2.3.3. Sizzle的Github主页 浏览器原生支持的元素查询方法: 方法名 方法描述 兼容性描述 getElementById 根据元素ID查 ...
- jQuery源码分析系列(三)Sizzle选择器引擎-下
选择函数:select() 看到select()函数,if(match.length === 1){}存在的意义是尽量简化执行步骤,避免compile()函数的调用. 简化操作同样根据tokenize ...
最新文章
- 澳洲服务器拳头账号怎么注册,lol手游东南亚服拳头账号注册教程 东南亚服怎么注册拳头账号[多图]...
- Zookeeper API 学习与使用
- 阿里云使用idea通过hdfs api来上传文件时出现could only be written to 0 of the 1 minReplication nodes.错误
- 懂得一些基本常识,就不会被《非酒精類致命飲料》或者叫做《我一辈子都不再喝可口可乐》的这篇文章所蒙蔽...
- python 调用opencv dll_【问题已解决】python 3.6下安装opencv-python解决cv2 DLL load failed: 找不到指定的模块问题...
- 矩阵分析——第一章 线形空间和线性变换
- ROS学习笔记基础1(Ubuntu16.04安装ROS和依赖包)
- [转载] 用python 获取当前时间(年-月-日 时:分:秒),并且返回当前时间的下一秒
- Java学习(11-15天, 线性数据结构)
- java数据结构编程问题_Java语言程序设计与数据结构第十一版(基础篇)第一章编程练习题答案...
- iredmail mysql_iRedmail配置手册
- Rayman的绝顶之路——Leetcode每日一题打卡6
- CSDN问答模块标题推荐任务(一) —— 基本框架的搭建
- 马克飞象 Markdown 使用和学习
- 人们对人工智能的看法(消极篇)
- Google Play评论抓取
- Kaggle文本语义相似度计算Top5解决方案分享
- pgsql日期及时间
- RGB24 To Yuv420 C语言实现
- Markdown使用小笔记