jQuery Sizzle选择器(一)
1、浏览器对css选择器采取逆向(从右向左)解析的原因:
如果正向解析,例如「div div p em」,我们首先就要检查当前元素到 html 的整条路径,找到最上层的div,再往下找,如果遇到不匹配就必须回到最上层那个 div,往下再去匹配选择器中的第一个 div,回溯若干次才能确定匹配与否,效率很低。
逆向匹配则不同,如果当前的 DOM 元素是 div,而不是 selector 最后的 em,那只要一步就能排除。只有在匹配时,才会不断向上找父节点进行验证。找到所有的em之后,再通过查找他的父元素是不是p来进行过滤。
2、Sizzle如果分解用户传入的css选择器字符串
以”div > div.cl p span.red“为例
在Sizzle内部封装了一个方法,该方法负责将css选择器分解为一个数组。数组中的每一项是一个对象,格式如下:
{
"type" : "CLASS",
"value" : ".red",
"matchs" : " "
}
看一下tokenize的源码:
// 假设传入进来的选择器是:div > p + .cl[type="checkbox"], #id:first-child
// 这里可以分为两个规则:div > p + .aaron[type="checkbox"] 以及 #id:first-child
// 返回的需要是一个Token序列
// Sizzle的Token格式如下 :{value:'匹配到的字符串', type:'对应的Token类型', matches:'正则匹配到的一个结构'}
function tokenize( selector, parseOnly ) {
var matched, match, tokens, type,
soFar, groups, preFilters,
cached = tokenCache[ selector + " " ];
// 这里的soFar是表示目前还未分析的字符串剩余部分
// groups表示目前已经匹配到的规则组,在这个例子里边,groups的长度最后是2,存放的是每个规则对应的Token序列
// 如果cache里边有,直接拿出来即可
if ( cached ) {
return parseOnly ? 0 : cached.slice( 0 );
}
// 初始化
soFar = selector;
// 这是最后要返回的结果,一个二维数组
// 有多少个并联选择器,里面就有多少个数组,数组里面是拥有value与type的对象
groups = [];
// 这里的预处理器为了对匹配到的Token适当做一些调整
// 自行查看源码,其实就是正则匹配到的内容的一个预处理
preFilters = Expr.preFilter;
// 递归检测字符串
// 比如"div > p + .cl input[type="checkbox"]"
while ( soFar ) {
// 以第一个逗号切割选择符,然后去掉前面的部分,处理同时传入多个同级选择器的情况,例如:$( "div, span" )
if ( !matched || (match = rcomma.exec( soFar )) ) {
if ( match ) {
// 如果匹配到逗号,将soFar中匹配到的部分删除掉
soFar = soFar.slice( match[0].length ) || soFar;
}
// 往规则组里边压入一个Token序列,目前Token序列还是空的
groups.push( tokens = [] );
}
// 将matched重置为false,为下次判断soFar中是否有内容做准备
matched = false;
// 将刚才前面的部分以关系选择器再进行划分
// 先处理这几个特殊的Token : >, +, 空格, ~
// 因为他们比较简单,并且是单字符的
if ( (match = rcombinators.exec( soFar )) ) {
// 获取到匹配的字符
matched = match.shift();
// 放入Token序列中
tokens.push({
value: matched,
type: match[0].replace( rtrim, " " )
});
// 剩余还未分析的字符串需要减去这段已经分析过的
soFar = soFar.slice( matched.length );
}
// 这里开始分析这几种Token : TAG, ID, CLASS, ATTR, CHILD, PSEUDO, NAME
// 将每个选择器组依次用ID,TAG,CLASS,ATTR,CHILD,PSEUDO这些正则进行匹配
// Expr.filter里边对应地 就有这些key
//如果通过正则匹配到了Token格式:match = matchExpr[ type ].exec( soFar )
//然后看看需不需要预处理:!preFilters[ type ]
//如果需要 ,那么通过预处理器将匹配到的处理一下 : match = preFilters[ type ]( match )
for ( type in Expr.filter ) {
if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
(match = preFilters[ type ]( match ))) ) {
matched = match.shift();
//放入Token序列中
tokens.push({
value: matched,
type: type,
matches: match
});
//剩余还未分析的字符串需要减去这段已经分析过的
soFar = soFar.slice( matched.length );
}
}
//如果到了这里都还没matched到,那么说明这个选择器在这里有错误
//直接中断词法分析过程
//这就是Sizzle对词法分析的异常处理
if ( !matched ) {
break;
}
}
//放到tokenCache函数里进行缓存
//如果只需要这个接口检查选择器的合法性,直接就返回soFar的剩余长度,倘若是大于零,说明选择器不合法
//其余情况,如果soFar长度大于零,抛出异常;否则把groups记录在cache里边并返回,
return parseOnly ?
soFar.length :
soFar ?
Sizzle.error( selector ) :
tokenCache( selector, groups ).slice( 0 );
}
jQuery Sizzle选择器(一)的更多相关文章
- [转]JQuery - Sizzle选择器引擎原理分析
原文: https://segmentfault.com/a/1190000003933990 ---------------------------------------------------- ...
- jQuery Sizzle选择器(二)
自己开始尝试读Sizzle源码. 1.Sizzle同过自执行函数的方式为自己创建了一个独立的作用域,它可以不依赖于jQuery的大环境而独立存在.因此它可以被应用到其它js库中.实现如下:(fun ...
- jQuery Sizzle选择器(三)
在Sizzle的入口方法Sizzle()中看到的一个根据浏览器来初始化document各个方法的函数setDocument(),接下来主要看一下这个方法都做了什么. 但之前有必要看一下它用到的一些Si ...
- jQuery源码分析系列(三)Sizzle选择器引擎-下
选择函数:select() 看到select()函数,if(match.length === 1){}存在的意义是尽量简化执行步骤,避免compile()函数的调用. 简化操作同样根据tokenize ...
- jQuery-1.9.1源码分析系列(三) Sizzle选择器引擎——编译原理
这一节要分析的东东比较复杂,篇幅会比较大,也不知道我描述后能不能让人看明白.这部分的源码我第一次看的时候也比较吃力,现在重头看一遍,再分析一遍,看能否查缺补漏. 看这一部分的源码需要有一个完整的概念后 ...
- JQuery Sizzle引擎源代码分析
最近在拜读艾伦在慕课网上写的JQuery课程,感觉在国内对JQuery代码分析透彻的人没几个能比得过艾伦.有没有吹牛?是不是我说大话了? 什么是Sizzle引擎? 我们经常使用JQuery的选择器查询 ...
- Sizzle选择器引擎介绍
一.前言 Sizzle原来是jQuery里面的选择器引擎,后来逐渐独立出来,成为一个独立的模块,可以自由地引入到其他类库中.我曾经将其作为YUI3里面的一个module,用起来畅通无阻,没有任何障碍. ...
- JavaScipt 源码解析 Sizzle选择器
jQuery的定位就是一个DOM的操作库,那么可想而知选择器是一个至关重要的模块.Sizzle,作为一个独立全新的选择器引擎,出现在jQuery 1.3版本之后,并被John Resig作为一个开源的 ...
- 关于jquery ID选择器的一点看法
最近看到一道前端面试题: 请优化selector写法:$(".foo div#bar:eq(0)") 我给出的答案会是: 1. $("#bar") 2. $( ...
随机推荐
- POI导出Word插入复选框
POI功能比较强大,但是有些不常用功能比如插入特殊符号,不知道API怎么调用 Word里要插入复选框,首先想到的是POI有没有提供现成的API,搜了一番,貌似都说不直接支持 http://stacko ...
- 转载:erlang实现安卓和IOS的推送。
erlang-百度云推送Android服务端功能实现-erlang erlang -- ios apns provider -- erlang 实现 转自:http://www.cnblogs.com ...
- FusionCancer-人类癌症相关的融合基因的数据库
RNA-seq 测序可以用于融合基因的发现,在过去的十几年里,RNA-seq 测序数据不断增加,发现的融合基因的数据也不断增加: FusionCancer 是一个人类癌症相关的融合基因的数据库,利用N ...
- (个人)Linux基本指令收集
1. 删除文件 其中 -r为向下递归删除 -f为强行删除,不做提示 rm -rf name 1 1 rm -rf name 2. 目录跳转指令 cd .. --跳转到上一级 cd ../ - ...
- Android仿腾讯手机管家实现桌面悬浮窗小火箭发射的动画效果
功能分析: 1.小火箭游离在activity之外,不依附于任何activity,不管activity是否开启,不影响小火箭的代码逻辑,所以小火箭的代码逻辑是要写在服务中: 2.小火箭挂载在手机窗体之上 ...
- 【伪装位置神器】神行者AnyLocation 1.3.0001可用于微信,陌陌
<ignore_js_op> 软件名称:神行者(破解)软件版本:v1.3.0001授权类别:免费测试机型:大可乐手机 下载链接: http://pan.baidu.com/s/1qWwSM ...
- Node.js之exports与module.exports
每一个node.js执行文件,都自动创建一个module对象,同时,module对象会创建一个叫exports的属性,初始化的值是 {} module.exports = {}; Node.js为了方 ...
- geoserver REST使用
1.部署一个简单的测试环境 测试geoserver REST接口,我们可使用python来测试,很方便.需要下载包: python,http://python.org/.我下载的是Python27版本 ...
- VC++调用MSFlexGrid的SetRow方法,出现异常“Invalid Row Value”
MSFlexGrid是微软提供的网格表格控件,SetRow方法用于设置当前焦点所在行. C++ Code 12345 void CMSFlexGrid::SetRow(long nNewVal ...
- Apache+php5
.下载回来的是解压文件,解压好放到要安装的位置.(我这里以D:\Acpache24为例) .打开Apache24\conf下httpd.conf 文件,用记事本打开即可. ()第37行ServerRo ...