對jQuery的Sizzle各方法做了深入分析(同時也參考了一些網上資料)后,將結果分享給大家。我將采用連載的方式,對Sizzle使用的一些方法詳細解釋一下,每篇文章介紹一個方法。
若需要轉載,請寫明出處,多謝。
/* * Sizzle方法是Sizzle選擇器包的主要入口,jQuery的find方法就是調用該方法獲取匹配的節點 * 該方法主要完成下列任務: * 1、對于單一選擇器,且是ID、Tag、Class三種類型之一,則直接獲取并返回結果 * 2、對于支持querySelectorAll方法的瀏覽器,通過執行querySelectorAll方法獲取并返回匹配的DOM元素 * 3、除上之外則調用select方法獲取并返回匹配的DOM元素 *  *  * @param selector 選擇器字符串 * @param context 執行匹配的最初的上下文(即DOM元素集合)。若context沒有賦值,則取document。 * @param results 已匹配出的部分最終結果。若results沒有賦值,則賦予空數組。 * @param seed 初始集合 */function Sizzle(selector, context, results, seed) {	var match, elem, m, nodeType,	// QSA vars	i, groups, old, nid, newContext, newSelector;	/*	 * preferredDoc = window.document	 * 	 * setDocument方法完成一些初始化工作	 */	if ((context ? context.ownerDocument || context : preferredDoc) !== document) {		setDocument(context);	}	context = context || document;	results = results || [];	/*	 * 若selector不是有效地字符串類型數據,則直接返回results	 */	if (!selector || typeof selector !== "string") {		return results;	}	/*	 * 若context既不是document(nodeType=9),也不是element(nodeType=1),那么就返回空集合	 */	if ((nodeType = context.nodeType) !== 1 && nodeType !== 9) {		return [];	}	// 若當前過濾的是HTML文檔,且沒有設定seed,則執行if內的語句體	if (documentIsHTML && !seed) {		/* 		 * 若選擇器是單一選擇器,且是ID、Tag、Class三種類型之一,則直接獲取并返回結果		 * 		 * rquickExpr = /^(?:#([/w-]+)|(/w+)|/.([/w-]+))$/		 * 上述正則表達式括號內三段依次分別用來判斷是否是ID、TAG、CLASS類型的單一選擇器		 * 上述正則表達式在最外層圓括號內有三個子表達式(即三個圓括號括起來的部分),		 *   分別代表ID、Tag、Class選擇器的值,在下面代碼中,分別體現在match[1]、match[2]、match[3]		 */		if ((match = rquickExpr.exec(selector))) {			// Speed-up: Sizzle("#ID")			// 處理ID類型選擇器,如:#ID			if ((m = match[1])) {				// 若當前上下文是一個document,則執行if內語句體				if (nodeType === 9) {					elem = context.getElementById(m);					// Check parentNode to catch when Blackberry 4.6					// returns					// nodes that are no longer in the document #6963					if (elem && elem.parentNode) {						// Handle the case where IE, Opera, and Webkit						// return items						// by name instead of ID						/*						 * 一些老版本的瀏覽器會把name當作ID來處理,						 * 返回不正確的結果,所以需要再一次對比返回節點的ID屬性						 */ 						if (elem.id === m) {							results.push(elem);							return results;						}					} else {						return results;					}				} else {					// Context is not a document					/*					 * contains(context, elem)用來確認獲取的elem是否是當前context對象的子對象					 */					if (context.ownerDocument							&& (elem = context.ownerDocument.getElementById(m))							&& contains(context, elem) && elem.id === m) {						results.push(elem);						return results;					}				}				// Speed-up: Sizzle("TAG")				// 處理Tag類型選擇器,如:SPAN			} else if (match[2]) {				push.apply(results, context.getElementsByTagName(selector));				return results;				// Speed-up: Sizzle(".CLASS")				/*				 * 處理class類型選擇器,如:.class				 * 下面條件判斷分別是:				 * m = match[3]:有效的class類型選擇器				 * support.getElementsByClassName 該選擇器的div支持getElementsByClassName				 * context.getElementsByClassName 當前上下文節點有getElementsByClassName方法				 * 				 */ 							} else if ((m = match[3]) && support.getElementsByClassName					&& context.getElementsByClassName) {				push.apply(results, context.getElementsByClassName(m));				return results;			}		}		// QSA path		/*		 * 若瀏覽器支持querySelectorAll方法且選擇器符合querySelectorAll調用標準,則執行if內語句體		 * 在這里的檢查僅僅是簡單匹配		 * 第一次調用Sizzle時,rbuggyQSA為空		 * 		 * if語句體內對當前context對象的id的賦值與恢復,是用來修正querySelectorAll的一個BUG		 * 該BUG會在某些情況下把當前節點(context)也作為結果返回回來。		 * 具體方法是,在現有的選擇器前加上一個屬性選擇器:[id=XXX],		 * XXX 為context的id,若context本身沒有設置id,則給個默認值expando。		 */				if (support.qsa && (!rbuggyQSA || !rbuggyQSA.test(selector))) {			nid = old = expando;			newContext = context;			// 若context是document,則newSelector取自selector,否則為false			newSelector = nodeType === 9 && selector;			// qSA works strangely on Element-rooted queries			// We can work around this by specifying an extra ID on the			// root			// and working up from there (Thanks to Andrew Dupont for			// the technique)			// IE 8 doesn't work on object elements			if (nodeType === 1 && context.nodeName.toLowerCase() !== "object") {				groups = tokenize(selector);				if ((old = context.getAttribute("id"))) {					/*					 * rescape = /'|///g,					 * 這里將old中的單引號、豎杠、反斜杠前加一個反斜杠					 * old.replace(rescape, "http://$&")代碼中的$&代表匹配項					 */					nid = old.replace(rescape, "http://$&");				} else {					context.setAttribute("id", nid);				}				nid = "[id='" + nid + "'] ";				// 重新組合新的選擇器				i = groups.length;				while (i--) {					groups[i] = nid + toSelector(groups[i]);				}				/*				 * rsibling = new RegExp(whitespace + "*[+~]")				 * rsibling用于判定選擇器是否存在兄弟關系符				 * 若包含+~符號,則取context的父節點作為當前節點				 */				newContext = rsibling.test(selector) && context.parentNode						|| context;				newSelector = groups.join(",");			}			if (newSelector) {				/*				 * 這里之所以需要用try...catch,				 * 是因為jquery所支持的一些選擇器是querySelectorAll所不支持的,				 * 當使用這些選擇器時,querySelectorAll會報非法選擇器,				 * 故需要jquery自身去實現。				 */				try {					// 將querySelectorAll獲取的結果并入results,而后返回resulsts					push.apply(results, newContext							.querySelectorAll(newSelector));					return results;				} catch (qsaError) {				} finally {					if (!old) {						context.removeAttribute("id");					}				}			}		}	}	// All others	// 除上述快捷方式和調用querySelectorAll方式直接獲取結果外,其余都需調用select來獲取結果	/*	 * rtrim = new RegExp("^" + whitespace + "+|((?:^|[^////])(?:////.)*)"	 *			+ whitespace + "+$", "g"),	 * whitespace = "[//x20//t//r//n//f]";	 * 上述rtrim正則表達式的作用是去掉selector兩邊的空白,空白字符由whitespace變量定義	 * rtrim的效果與new RegExp("^" + whitespace + "+|" + whitespace + "+$", "g")相似	 */	return select(selector.replace(rtrim, "$1"), context, results, seed);}各位朋友,若覺得寫得不錯,幫我頂一下,給點動力,多謝!
新聞熱點
疑難解答