Sizzle.selectors.relative 源码分析!

1 jQuery 对象Sizzle.selectors.relative中存放了块间关系符和对应的块间关系过滤函数,称为“块间关系过滤函数集”

块间关系符共有4种,其含义和过滤方式如图所示。

在函数Sizzle( selector, context, results, seed )从右向左进行过滤时,块间关系过滤函数被

调用,用于检查映射集checkSet中的元素是否匹配块间关系符左侧的块表达式。调用时的参

数格式为:

Sizzle.selectors.relative[ 块间关系符 cur  ](  映射集 checkSet,  左侧块表达式pop,
contextXML );

块间关系过滤函数接受3个参数:

‰ ‰参数checkSet:映射集,对该元素集合执行过滤操作。

‰ ‰参数part:大多数情况下是块间关系符左侧的块表达式,该参数也可以是DOM元素。

‰ ‰参数isXML:布尔值,指示是否运行在一个XML文档中。

块间关系过滤函数实现的3个关键步骤如下:

1)遍历映射集checkSet。

2)按照块间关系符查找每个元素的兄弟元素、父元素或祖先元素。

3)检查找到的元素是否匹配参数part,并替换映射集checkSet中对应位置的元素。

a. 如果参数part是标签,则检查找到的元素其节点名称nodeName是否与之相等,

如果相等则替换为找到的元素,不相等则替换为false。

b. 如果参数part是DOM元素,则检查找到的元素是否与之相等,如果相等则替换

为true,不相等则替换为false。

c. 如果参数part是非标签字符串,则调用方法Sizzle.filter( selector, set, inplace, not )过滤。

也就是说,遍历结束后,映射集checkSet中的元素可能会是兄弟元素、父元素、

祖先元素、true或false。

1 块间关系符"+"匹配选择器"prev + next",即匹配所有紧接在元素prev后的兄弟元素

next。例如,$("div + span")、$(".lastdiv + span")。对于从右向左的查找方式,则是检查元

素next之前的兄弟元素是否匹配块表达式prev。

var Expr = Sizzle.selectors = {
	relative: {
		"+": function(checkSet, part){
			//检查参数是否为字符串
			var isPartStr = typeof part === "string",
			//指示参数part是否为标签字符串
			isTag = isPartStr && !rNonWord.test( part ),
			//isPartStrNotTag:指示参数part是否是非标签字符串。
			isPartStrNotTag = isPartStr && !isTag;

			if ( isTag ) {
			part = part.toLowerCase();
			}
			/*
			遍历映射集checkSet,查找每个元素的前一个兄弟元素,并替换映
			射集checkSet中对应位置的元素,有以下3个逻辑分支:
			?1 如果未找到兄弟元素,则替换为false。
			2 ?如果找到了兄弟元素,并且参数part是标签,则检查兄弟元素的节点名称nodeName
			是否与之相等,如果相等则替换为兄弟元素,不相等则替换为false。
			3 ?如果找到了兄弟元素,并且参数part是DOM元素,则检查二者是否相等,如果相等
			则替换为true,不相等则替换为false。
			因此,在遍历结束后,映射集checkSet中的元素可能会是兄弟元素、true或false。
						*/
			for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
				if ( (elem = checkSet[i]) ) {
					/*在遍历兄弟元素的同时过滤掉非元素节点,并且只要取到一个兄弟元素就
退出while循环。*/
					while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {

					}

		 			checkSet[i] = isPartStrNotTag || elem && elem.node
					Name.toLowerCase() === part ?
					elem || false :
					elem === part;
				}
			}
			/*
			如果参数part是非标签字符串,则调用方法Sizzle.filter( selector,
			set, inplace, not )过滤映射集checkSet。对于参数part是标签和DOM元素的情况,在前面遍
			历映射集checkSet时已经处理过了。
			*/
			if ( isPartStrNotTag ) {
				Sizzle.filter( part, checkSet, true );
			}
		},
	},
};

2 块间关系符">"用于选择器"parent > child",即匹配父元素parent下的子元素child。

例如,$("div + span")、$(".lastdiv + span")。对于从右向左的查找方式,则是检查子元素

child的父元素是否匹配块表达式parent。

var Expr = Sizzle.selectors = {
	relative: {
		">": function( checkSet, part ) {
			var elem,
			isPartStr = typeof part === "string",
			i = 0,
			l = checkSet.length;
			/*
			如果参数part是标签,则遍历映射集checkSet,查找每个元素的
			父元素,并检查父元素的节点名称nodeName是否与参数part相等,如果相等则替换映射集
			checkSet中对应位置的元素为父元素,不相等则替换为false。
			*/
			if ( isPartStr && !rNonWord.test( part ) ) {
				part = part.toLowerCase();

				for ( ; i < l; i++ ) {
					elem = checkSet[i];

					if ( elem ) {
						var parent = elem.parentNode;
						checkSet[i] = parent.nodeName.toLowerCase() === part ?
						parent : false;
					}
				}
			} else {
			/*
			如果参数part不是标签,则可能是非标签字符串或DOM元素,同
			样遍历映射集checkSet,查找每个元素的父元素,并替换映射集checkSet中对应位置的元
			素,在这个过程中有以下2个逻辑分支:
			?1 ?如果参数part是非标签字符串,则在遍历映射集checkSet的过程中,替换映射集
			checkSet中对应位置的元素为父元素,遍历结束后调用方法Sizzle.filter( selector, set,
			inplace, not )过滤映射集checkSet。
			?2 ?如果参数part是元素,则在遍历映射集checkSet时,检查每个元素的父元素是否与
			之相等,如果相等则替换映射集checkSet中对应位置的元素为true,不相等则替换为
			false。
			因此,在遍历结束后,映射集checkSet中的元素可能会是父亲元素、true或false。
			*/
				for ( ; i < l; i++ ) {
					elem = checkSet[i];

					if ( elem ) {
						checkSet[i] = isPartStr ?
						elem.parentNode :
						elem.parentNode === part;
					}
				}

				if ( isPartStr ) {
					Sizzle.filter( part, checkSet, true );
				}
			}
		},
	},
};

3 块间关系符""用于选择器"ancestor descendant",即匹配祖先元素ancestor的所有后代

元素descendant。例如,$("div button")、$("div .btn")。对于从右向左的查找方式,则是检

查后代元素descendant的祖先元素是否匹配块表达式ancestor。

var Expr = Sizzle.selectors = {
	relative: {
		"": function(checkSet, part, isXML){
			var nodeCheck,
			doneName = done++,
			checkFn = dirCheck;
			/*
			?1 如果参数part是非标签字符串或DOM元素,则调用函数dirCheck()过滤映射集
			checkSet。
			?2 如果参数part是标签,则调用函数dirNodeCheck()过滤映射集checkSet。
			调用函数dirCheck()和dirNodeCheck()时的参数格式为:
			checkFn( 方向 "parentNode/previousSibling", 块表达式 part, 缓存计数器 doneName, 映
			射集 checkSet, nodeCheck, isXML )
			函数dirCheck()和dirNodeCheck()会遍历映射集checkSet,查找每个元素的祖先元素,
			并检查是否有祖先元素匹配参数part,同时替换映射集checkSet中对应位置的元素。
			*/
			if ( typeof part === "string" && !rNonWord.test( part ) ) {
				part = part.toLowerCase();
				nodeCheck = part;
				checkFn = dirNodeCheck;
			}

			checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML );
		},
	},
};

4 块间关系符"~"用于选择器"prev~siblings",即匹配元素prev之后的所有兄弟元

素siblings。例如,$(‘div~p‘)。对于从右向左的查找方式,则是检查元素siblings之前的

兄弟元素是否匹配块表达式prev。

Sizzle.selectors.relative["~"]( checkSet, part )的源码实现与Sizzle.selectors.relative[""]

( checkSet, part )几乎一样,两者的区别仅仅在于调用函数dirCheck()和dirNodeCheck()时第

一个参数的值不同,前者是"previousSibling",后者则是"parentNode"。

相关代码如下所示:

var Expr = Sizzle.selectors = {
	relative: {
		"~": function( checkSet, part, isXML ) {
			var nodeCheck,
			doneName = done++,
			checkFn = dirCheck;

			if ( typeof part === "string" && !rNonWord.test( part ) ) {
				part = part.toLowerCase();
				nodeCheck = part;
				checkFn = dirNodeCheck;
			}

			checkFn( "previousSibling", part, doneName, checkSet, nodeCheck,
			isXML );
		}
	},
};
时间: 2024-10-12 19:59:54

Sizzle.selectors.relative 源码分析!的相关文章

Android视图View绘制流程与源码分析(全)

来源:[工匠若水 http://blog.csdn.net/yanbober] 1 背景 还记得前面<Android应用setContentView与LayoutInflater加载解析机制源码分析>这篇文章吗?我们有分析到Activity中界面加载显示的基本流程原理,记不记得最终分析结果就是下面的关系: 看见没有,如上图中id为content的内容就是整个View树的结构,所以对每个具体View对象的操作,其实就是个递归的实现. 前面<Android触摸屏事件派发机制详解与源码分析一(

jquery源码分析(二)——结构

再来复习下整体架构: jQuery源码分析(基于 jQuery 1.11 版本,共计8829行源码) (21,94)                定义了一些变量和函数jQuery=function(){} (96,280)        给jQuery添加一些方法和属性,jQuery.fn=jQuery.prototype(285,347)        extend:        jQuery的一些继承方法        更容易进行后续的扩展                       

jQuery源码分析-jQuery中的循环技巧

Js代码   作者:nuysoft/JS攻城师/高云 QQ:47214707 EMail:[email protected] 声明:本文为原创文章,如需转载,请注明来源并保留原文链接. 前记:本文收集了jQuery中出现的各种遍历技巧和场景 Js代码   // 简单的for-in(事件) for ( type in events ) { } Js代码   // 缓存length属性,避免每次都去查找length属性,稍微提升遍历速度 // 但是如果遍历HTMLCollection时,性能提升非常

OpenStack_Swift源码分析——创建Ring及添加设备源码详细分析

1 创建Ring 代码详细分析 在OpenStack_Swift--Ring组织架构中我们详细分析了Ring的具体工作过程,下面就Ring中增加设备,删除设备,已经重新平衡的实现过程作详细的介绍. 首先看RingBuilder类 def __init__(self, part_power, replicas, min_part_hours): #why 最大 2**32 if part_power > 32: raise ValueError("part_power must be at

Spring源码分析——资源访问利器Resource之接口和抽象类分析

从今天开始,一步步走上源码分析的路.刚开始肯定要从简单着手.我们先从Java发展史上最强大的框架--Spring...旗下的资源抽象接口Resource开始吧. 我看了好多分析Spring源码的,每每一开始就是Spring IOC.AOP.BeanFactory这样的Spring典型模块,实在看厌了,这些暂且留到以后.我的想法是,分析就分析别人没分析过的,或者以不同的角度来分析别人分析过的. 可能很多用了Spring多年的程序员对Resource都了解有限,毕竟访问资源一般是搭建web工程框架的

Solr4.9.0源码分析(2)之Solr的启动(一)

上文写到Solr的启动过程是在SolrDispatchFilter的init()里实现,当Tomcat启动时候会自动调用init(); Solr的启动主要在 this.cores = createCoreContainer();语句中实现. /** *初始化,当tomcat启动时候开始初始化,其中主要调用createCoreContainer来实现Solr的初始化 */ public void init(FilterConfig config) throws ServletException {

jQuery.lazyload使用及源码分析

前言: 貌似以前自己也写过图片懒加载插件,但是新公司使用的是jQuery.lazyload插件,为了更好的运用,自己还是把源码看了遍,分别记录了如何使用, 插件原理,各个配置属性的完整解释,demo实例,源码分析(较简短),源码分析可以配合使用,配置属性,原理进行阅读,如需转载,请注明出处 博客园 华子yjh 一.如何使用 // 最简单的使用,不带参数 $('img').lazyload(); // 带参数(配置对象),下面配置对象中的各个属性值都是默认的 $('img').lazyload({

jquery2源码分析系列目录

学习jquery的源码对于提高前端的能力很有帮助,下面的系列是我在网上看到的对jquery2的源码的分析.等有时间了好好研究下.我们知道jquery2开始就不支持IE6-8了,从jquery2的源码中可以学到很多w3c新的标准( 如html5,css3,ECMAScript).原文地址是:http://www.cnblogs.com/aaronjs/p/3279314.html 关于1.x.x版的jquery源码分析系列,本博客也转载了一个地址http://www.cnblogs.com/jav

jQuery源码分析1

写在开头: 昨天开始,我决定要认真的看看jQuery的源码,选择1.7.2,源于公司用的这个版本.由于源码比较长,这将会是一个比较持久的过程,我将要利用业余时间,和偶尔上班不算忙的时间来进行.其实原本是打算对着源码抄一遍,将对其的理解写成注释,这也算是在强行堆代码量了吧(我想我这是有多懒,必须要反省).不过鉴于自己平时比较懒惰的可耻行径,和太多的东西都写在一起有点庞大,我想我还是有必要写成一个专栏,来记录这个过程.其实最根本的原因是:源码里都是有注释的,而且注释写得那么详尽,翻译过来就是了,但是