前序
概要
浅析源码
var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,
((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~]) (\s*,\s*)? ((?:.|\r|\n)*)
1. (?:\((?:\([^()]+\) 2.[^()]+)+\) 3. \[(?:\[[^[\]]*\] 4. [^[\]]+)+\]|\\. 5.[^ >+~,(\[]+)+ 6.[>+~]
1.先查找页面上所有的div 2.循环所有的div,查找每个div下的p 3.合并结果
1.先查找页面上所有的p 2.循环所有的p,查找每个p的父元素 1.如果不是div,遍历上一层。 2.如果已经是顶层,排除此p。 3.如果是div,则保存此p元素。
/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/ //POS的值
当处于1的情况时:
//如果存在伪类选择符,从selector中移除,并保存在later中
// 这样一来,匹配对象便分离出来:selector(简单选择符存储器)和later(伪类选择符存储器)。
while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
later += match[0];
selector = selector.replace( Expr.match.PSEUDO, "" );
}
//构造selector,并调用Sizzle进行匹配,将结果存储在tmpSet中
selector = Expr.relative[selector] ? selector + "*" : selector;
for ( var i = 0, l = root.length; i < l; i++ ) {
Sizzle( selector, root[i], tmpSet );
}
// 最后便是filter的过滤
return Sizzle.filter( later, tmpSet );
// 这样一来,匹配对象便分离出来:selector(简单选择符存储器)和later(伪类选择符存储器)。
while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
later += match[0];
selector = selector.replace( Expr.match.PSEUDO, "" );
}
//构造selector,并调用Sizzle进行匹配,将结果存储在tmpSet中
selector = Expr.relative[selector] ? selector + "*" : selector;
for ( var i = 0, l = root.length; i < l; i++ ) {
Sizzle( selector, root[i], tmpSet );
}
// 最后便是filter的过滤
return Sizzle.filter( later, tmpSet );
//这个为特例,被正则分割的A数组长度为2,则合并数组元素,上下文则原封不动为Sizzle传递进来的context。
if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
// 完成一次匹配, 由posProcess 内部调用 filter进行匹配
// 但在匹配前,完成了一次连接选择符的操作
// 存入set,注 set 当前还不是最终的结果,其这里的set和上面的tmpSet一样,都是一个"暂时性"的结果集
set = posProcess( parts[0] + parts[1], context );
if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
// 完成一次匹配, 由posProcess 内部调用 filter进行匹配
// 但在匹配前,完成了一次连接选择符的操作
// 存入set,注 set 当前还不是最终的结果,其这里的set和上面的tmpSet一样,都是一个"暂时性"的结果集
set = posProcess( parts[0] + parts[1], context );
set = Expr.relative[ parts[0] ] ?
[ context ] :
// 否则对队列首元素进行一次简单匹配操作
Sizzle( parts.shift(), context );
[ context ] :
// 否则对队列首元素进行一次简单匹配操作
Sizzle( parts.shift(), context );
while ( parts.length ) {
// 依次对 所匹配到的 数组中元素进行 递进匹配
selector = parts.shift();
// '>' -> '>input' 的形式
if ( Expr.relative[ selector ] )
selector += parts.shift();
set = posProcess( selector, set );
// 依次对 所匹配到的 数组中元素进行 递进匹配
selector = parts.shift();
// '>' -> '>input' 的形式
if ( Expr.relative[ selector ] )
selector += parts.shift();
set = posProcess( selector, set );
当处于2的情况时:
//为ret绑定正确的返回值
var ret = seed ? //seed 为上一次调用sizzle返回值, 即前文中提到的set|tmpset
//将预匹配后的A数组(parts)中的最后元素设置为ret的expr属性,set属性设为上一次匹配的结果集。
{ expr: parts.pop(), set: makeArray(seed) } :
//如果是第一次调用,则进行匹配操作,调用find函数
// 以parts数组最末元素为当前选择符,进行匹配操作,同时设置与之相关的context
Sizzle.find( parts.pop(), parts.length === 1 && context.parentNode ? context.parentNode : context, isXML(context) );
var ret = seed ? //seed 为上一次调用sizzle返回值, 即前文中提到的set|tmpset
//将预匹配后的A数组(parts)中的最后元素设置为ret的expr属性,set属性设为上一次匹配的结果集。
{ expr: parts.pop(), set: makeArray(seed) } :
//如果是第一次调用,则进行匹配操作,调用find函数
// 以parts数组最末元素为当前选择符,进行匹配操作,同时设置与之相关的context
Sizzle.find( parts.pop(), parts.length === 1 && context.parentNode ? context.parentNode : context, isXML(context) );
if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
// ...
// 如果支持直接获取,则将获取class的方法 直接添加进 Expr.order中 ['ID', 'NAME', 'TAG']
Expr.order.splice(1, 0, "CLASS");
//同时在find中追加对class的获取
Expr.find.CLASS = function(match, context, isXML) {
if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
return context.getElementsByClassName(match[1]);
}
};
})();
// ...
// 如果支持直接获取,则将获取class的方法 直接添加进 Expr.order中 ['ID', 'NAME', 'TAG']
Expr.order.splice(1, 0, "CLASS");
//同时在find中追加对class的获取
Expr.find.CLASS = function(match, context, isXML) {
if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
return context.getElementsByClassName(match[1]);
}
};
})();
//order: [ "ID", "NAME", "TAG" ]
// 当然,如果浏览器支持对class的直接获取时,order中就会出现class的相关匹配规则
for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
var type = Expr.order[i], match;
// 根据 type 对所传进来的expr 进行正则匹配
// match中通过正则限制了这三类匹配方式的条件。
// 1. ID: /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
// 2. NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,
// 3. TAG: /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,
if ( (match = Expr.match[ type ].exec( expr )) ) {
var left = RegExp.leftContext;
//保证返回结果的正确性,如果存在\,则删除
if ( left.substr( left.length - 1 ) !== "\\" ) {
match[1] = (match[1] || "").replace(/\\/g, "");
// 根据type调用 sizzle.selector.find方法获取结果集。
set = Expr.find[ type ]( match, context, isXML );
if ( set != null ) {
//如果匹配成功,删除已经匹配的expr
expr = expr.replace( Expr.match[ type ], "" );
break;
}
}
}
}
return {set: set, expr: expr};
};
// 当然,如果浏览器支持对class的直接获取时,order中就会出现class的相关匹配规则
for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
var type = Expr.order[i], match;
// 根据 type 对所传进来的expr 进行正则匹配
// match中通过正则限制了这三类匹配方式的条件。
// 1. ID: /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
// 2. NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,
// 3. TAG: /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,
if ( (match = Expr.match[ type ].exec( expr )) ) {
var left = RegExp.leftContext;
//保证返回结果的正确性,如果存在\,则删除
if ( left.substr( left.length - 1 ) !== "\\" ) {
match[1] = (match[1] || "").replace(/\\/g, "");
// 根据type调用 sizzle.selector.find方法获取结果集。
set = Expr.find[ type ]( match, context, isXML );
if ( set != null ) {
//如果匹配成功,删除已经匹配的expr
expr = expr.replace( Expr.match[ type ], "" );
break;
}
}
}
}
return {set: set, expr: expr};
};
while ( parts.length ) {
var cur = parts.pop(), pop = cur;
// 是否存在 类似这样的匹配 eg: '+', '>'等
if ( !Expr.relative[ cur ] ) {
cur = "";
} else {
//如果存在层间关系的约束 则修复 cur 和pop的指向
// eg ['div', '+', 'span'] => pop = div; cur = '+'; 并进入 relative的匹配。
pop = parts.pop();
}
// 确保拥有上下文 代码略过
Expr.relative[ cur ]( checkSet, pop, isXML(context) );
}
var cur = parts.pop(), pop = cur;
// 是否存在 类似这样的匹配 eg: '+', '>'等
if ( !Expr.relative[ cur ] ) {
cur = "";
} else {
//如果存在层间关系的约束 则修复 cur 和pop的指向
// eg ['div', '+', 'span'] => pop = div; cur = '+'; 并进入 relative的匹配。
pop = parts.pop();
}
// 确保拥有上下文 代码略过
Expr.relative[ cur ]( checkSet, pop, isXML(context) );
}
实例
- jquery.init -> jquery.prototype.find
- 进入Sizzle(对xml的判断) -> 设置parts数组等在匹配中所需要的元素 -> 根据数组长度以及调用origPos进行判断,来决定进入哪个分支,在这个实例下进入分支1
- 循环调用Sizzle进行匹配,将结果存入set中(因为在这一过程中是循环调用,所以对Sizzle的判断也是需要多次,进入哪一分支当然也会是不一样的,比如第二轮循环判断则进入分支2中进行处理) ,对于>号的处理,也会将它合并在其后的span中,构成新的选择符 ‘>span’,然后进入Expr.relative进行匹配,同时调用posProcess。
- 调用Sizzle.find 匹配除伪类以外的部分(即这里的选择器不包含:last),首先会调用Expr.find的find方法来判断是否为哪一类匹配,在这一实例中,为TAG匹配。
- 对从4步中生成的对象进行过滤,匹配’>'(这一步的匹配是由Sizzle.filter触发,由Expr.relative完成),而在匹配’span:last’时则由posProcess来触发,设置later值(:first)以及selector(span),对span的匹配和4步骤一样,重复匹配,而对:first的匹配则是第5步的重头戏,也就是调用Sizzle.filter来完成, 由此便生成了最后的匹配结果。
1.对表达式分组。 2.选择合适的处理顺序。 3.在当前的上下文里过滤要找的节点。并更新上下文。重复这一过程,直到结尾。 4.对结果排序,如果需要的话。 5.返回结果数组。
前向兼容
querySelectorAll
//如果当前document 支持 querySelectorAll方法,则将浏览器可以完成的匹配完全交给浏览器
if ( document.querySelectorAll ) (function(){
var oldSizzle = Sizzle;
// 解决Safari bug 略过 ...
Sizzle = function(query, context, extra, seed){
context = context || document;
// 因为querySelectorAll 在domElement 节点上操作时,存在bug 所以多了这样的判断
// bug info: http://ejohn.org/blog/thoughts-on-queryselectorall/
if ( !seed && context.nodeType === 9 && !isXML(context) ) {
return makeArray( context.querySelectorAll(query), extra );
}
// querySelectorAll 可用则直接返回结果,否则才调用 sizzle
return oldSizzle(query, context, extra, seed);
};
// oldSizzle 方法追加进 新的 Sizzle 中
})();
if ( document.querySelectorAll ) (function(){
var oldSizzle = Sizzle;
// 解决Safari bug 略过 ...
Sizzle = function(query, context, extra, seed){
context = context || document;
// 因为querySelectorAll 在domElement 节点上操作时,存在bug 所以多了这样的判断
// bug info: http://ejohn.org/blog/thoughts-on-queryselectorall/
if ( !seed && context.nodeType === 9 && !isXML(context) ) {
return makeArray( context.querySelectorAll(query), extra );
}
// querySelectorAll 可用则直接返回结果,否则才调用 sizzle
return oldSizzle(query, context, extra, seed);
};
// oldSizzle 方法追加进 新的 Sizzle 中
})();
扩展
// filter的简写 ':'
jQuery.expr[":"] = jQuery.expr.filters;
$.extend($.expr[':'], {
hasSpan: function(e) {
return $(e).find('span').length > 0;
}
});
jQuery.expr[":"] = jQuery.expr.filters;
$.extend($.expr[':'], {
hasSpan: function(e) {
return $(e).find('span').length > 0;
}
});
//直接用就可以了
$('div:hasSpan')....
$('div:hasSpan')....
相关推荐
于是看了jquery的源码,jquery用的选择器的引擎是sizzle,是jquery的作者另一开源项目,在github上面有,号称最快的dom选择器!不到2000行代码。上面说了不是很精彩的开场白,我么来个 for example: $(‘.test’) 在...
jquery从1.3开始,使用了新的选择器–sizzle。效率超过了以前的jquery版本的其他选择器。下面这篇文章主要介绍了jQuery源中sizzle选择器的相关资料,文中介绍的很详细,需要的朋友可以参考借鉴,下面来一起看看吧。
近期看了一些网上关于Sizzle的分析文章,就匹配次序往往就说使用了从右到左的逆向匹配法,...在此先申明一点,下面所说的关系选择器是指W3C中的Combinator选择器,因本人觉得用关系选择器这个名字要比其它更加贴近实际
jQuery常规选择器源码,适合初学者哦,可以下载下来作为参考资料的。
Selenium WebDriver的扩展包括jQuery / Sizzle选择器支持。 产品特点 主要 支持嵌套选择器 易于设置:安装NuGet软件包并开始与您现有的Selenium解决方案一起使用 通过与Appveyor的持续集成设置,单元和集成测试以及...
用于收集和显示 Sizzle 选择器的控制台性能统计信息的 jQuery 插件。 Sizzle 通过$.find()集成到 jQuery 中,并用于所有接受选择器字符串的方法中,例如.filter(selector) 、 .closest(selector)等。 SizzleStats ...
* Sizzle方法是Sizzle选择器包的主要入口,jQuery的find方法就是调用该方法获取匹配的节点 * 该方法主要完成下列任务: * 1、对于单一选择器,且是ID、Tag、Class三种类型之一,则直接获取并返回结果 * 2、对于...
jQuery / Sizzle自定义伪选择器 只是我的伪装收藏。 表现 请注意将通过这些选择器传递多少个元素,因为它们将无法利用本机DOM querySelectorAll()方法提供的性能提升。 为了在使用这些伪指令中的任何一个时获得最佳...
此外,选择器引擎Sizzle修复了一些边缘问题和bug,包括对于多个选择符(~ > +)的改进、更好地检测浏览器bug等。 5. XSS防护 $()方法可以创建HTML元素,如果被用来传递一个[removed]标签,则可以运行脚本。...
jQuery最强大的功能在于它可以通过css选择器查找元素,它的源码中有一半是sizzle css选择器引擎的代码,在html5规范出来之后,增加了document.querySelector和document.querySelectorAll直接查找元素,如果是做...
1. Sizzle 选择器引擎重新架构 2. 重新改造动画处理 3. 自动 CSS 前缀处理 当你在 .css() 和 .animate() 中使用 CSS 属性时,jQuery 会自动根据浏览器类型来设置一些前缀,例如 .css("user-select", "none") 在 ...
在分析Sizzle源码之前,先整理一下选择器的工作原理 先明确一些选择器中用到的名词,后边阅读时不会有歧义: 选择器表达式: “div > p” 块表达式: “div” “p” 并列选择器表达式: “div, p” 块分割器: ...
资源名称:jQuery技术内幕:深入解析jQuery架构设计与...接着详细分析了底层支持模块的源码实现,包括:选择器 Sizzle、异步队列 Deferred、数据缓存 Data、资源太大,传百度网盘了,链接在附件中,有需要的同学自取。
本文实例讲述了高效jQuery选择器的5个技巧。分享给大家供大家参考,具体如下: 顾名思义,jQuery专注于查询(queries)。...在最坏的情况下,jQuery的Sizzle引擎必须解析选择器字符串来匹配元素。 下
本书循序渐进地讲解了jquery高效开发的方法和技巧,内容包括jquery框架的设计模式和思路、sizzle选择器的构成和工作机制、dom文档操作、事件处理、动画设计、ajax异步通信、插件扩展和辅助工具等。 执行效率是...
资源名称:犀利开发 jQuery内核详解与实践内容简介:本书循序渐进地讲解了jQuery高效开发的方法和技巧,内容包括jQuery框架的设计模式和思路、Sizzle选择器的构成和工作机制、DOM文档操作、事件处理、动画设计、Ajax...