Sizzle.selectors.relative [ 原始碼分析 ]

weixin_34377065發表於2014-10-28

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 );
		}
	},
};


相關文章