jQuery selector source code interpretation (a) : Sizzle method

  • 2020-05-24 05:10:21
  • OfStack

After an in-depth analysis of jQuery's Sizzle methods (as well as some online references), I will share the results with you. I will explain in detail some of the methods used by Sizzle in a serialized manner, one for each article.

If you need to reprint, please indicate the source, thank you.


/*
 * Sizzle Method is Sizzle The main entrance to the selector package, jQuery the find The method is called to get the matching node 
 *  The method mainly accomplishes the following tasks: 
 * 1 And for a single 1 Selector, and yes ID , Tag , Class3 Types of 1 , then directly get and return the result 
 * 2 , for support querySelectorAll Method of the browser by execution querySelectorAll Method gets and returns a match DOM The element 
 * 3 , is called except above select Method gets and returns a match DOM The element 
 * 
 * 
 * @param selector  Selector string 
 * @param context  The initial context (i.e DOM Element set. if context If there is no assignment, take document . 
 * @param results  The partial final result that has been matched. if results If there is no assignment, an empty array is assigned. 
 * @param seed  Initial set 
 */
function Sizzle(selector, context, results, seed) {
	var match, elem, m, nodeType,
	// QSA vars
	i, groups, old, nid, newContext, newSelector;

	/*
	 * preferredDoc = window.document
	 * 
	 * setDocument Methods to complete 1 Some initialization work 
	 */
	if ((context ? context.ownerDocument || context : preferredDoc) !== document) {
		setDocument(context);
	}

	context = context || document;
	results = results || [];

	/*
	 *  if selector String type data that is not valid, is returned directly results
	 */
	if (!selector || typeof selector !== "string") {
		return results;
	}

	/*
	 *  if context neither document ( nodeType=9 ), no element(nodeType=1) , then return the empty collection 
	 */
	if ((nodeType = context.nodeType) !== 1 && nodeType !== 9) {
		return [];
	}

	//  If the current filter is HTML Documentation, and no Settings seed , the implementation if Inside the body of a statement 
	if (documentIsHTML && !seed) {

		/* 
		 *  If the selector is single 1 Selector, and yes ID , Tag , Class3 Types of 1 , then directly get and return the result 
		 * 
		 * rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/
		 *  The above regular expression is enclosed in parentheses 3 Segments are used to determine whether or not ID , TAG , CLASS Types of single 1 The selector 
		 *  The above regular expression is inside the outermost parentheses 3 The subexpression (i.e 3 The part enclosed in parentheses), 
		 *    Represent the ID , Tag , Class The selector values, shown in the following code, are match[1] , match[2] , match[3]
		 */
		if ((match = rquickExpr.exec(selector))) {
			// Speed-up: Sizzle("#ID")
			//  To deal with ID Type selector, such as: #ID
			if ((m = match[1])) {
				//  If the current context is 1 a document , the implementation if The statement from 
				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
						/*
						 * 1 Some older versions of the browser will name As a ID To deal with, 
						 *  Returns incorrect results, so it needs to be repeated 1 Subcontrast returns the node ID attribute 
						 */ 
						if (elem.id === m) {
							results.push(elem);
							return results;
						}
					} else {
						return results;
					}
				} else {
					// Context is not a document
					/*
					 * contains(context, elem) Used to confirm the acquisition elem Is it current context object 
					 */
					if (context.ownerDocument
							&& (elem = context.ownerDocument.getElementById(m))
							&& contains(context, elem) && elem.id === m) {
						results.push(elem);
						return results;
					}
				}

				// Speed-up: Sizzle("TAG")
				//  To deal with Tag Type selector, such as: SPAN
			} else if (match[2]) {
				push.apply(results, context.getElementsByTagName(selector));
				return results;

				// Speed-up: Sizzle(".CLASS")
				/*
				 *  To deal with class Type selector, such as: .class
				 *  The following conditional judgments are: 
				 * m = match[3] : effective class Type selector 
				 * support.getElementsByClassName  Of the selector div support getElementsByClassName
				 * context.getElementsByClassName  The current context node does getElementsByClassName methods 
				 * 
				 */ 
				
			} else if ((m = match[3]) && support.getElementsByClassName
					&& context.getElementsByClassName) {
				push.apply(results, context.getElementsByClassName(m));
				return results;
			}
		}

		// QSA path
		/*
		 *  If the browser supports querySelectorAll Method and the selector matches querySelectorAll Invoke the standard, and execute if The statement from 
		 *  The check here is just a simple match 
		 *  The first 1 Time to call Sizzle When, rbuggyQSA Is empty 
		 * 
		 * if Statement body to the current context The object's id The assignment and recovery are used for correction querySelectorAll the 1 a BUG
		 *  the BUG In some cases, the current node ( context ) also returns as a result. 
		 *  This is done by adding it to an existing selector 1 Property selector: [id=XXX] . 
		 * XXX  for context the id If, context It's not set id , then give a default value expando . 
		 */
		
		if (support.qsa && (!rbuggyQSA || !rbuggyQSA.test(selector))) {
			nid = old = expando;
			newContext = context;
			//  if context is document , newSelector Taken from the selector , or for 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,
					 *  There will be old In single quotation marks, vertical, backslash before 1 A backslash 
					 * old.replace(rescape, "\\$&") In the code $& Representative match 
					 */
					nid = old.replace(rescape, "\\$&");
				} else {
					context.setAttribute("id", nid);
				}
				nid = "[id='" + nid + "'] ";

				//  Recombine the new selector 
				i = groups.length;
				while (i--) {
					groups[i] = nid + toSelector(groups[i]);
				}
				/*
				 * rsibling = new RegExp(whitespace + "*[+~]")
				 * rsibling Used to determine whether the selector has a sibling 
				 *  If contain +~ Sign, then take context Is the parent of the current node 
				 */
				newContext = rsibling.test(selector) && context.parentNode
						|| context;
				newSelector = groups.join(",");
			}

			if (newSelector) {
				/*
				 *  That's why we need it here try...catch . 
				 *  because jquery Supported by the 1 Some selectors are querySelectorAll Not supported, 
				 *  When you use these selectors, querySelectorAll Will report an illegal selector, 
				 *  So the need to jquery To do it yourself. 
				 */
				try {
					//  will querySelectorAll The results obtained are merged results And then come back resulsts
					push.apply(results, newContext
							.querySelectorAll(newSelector));
					return results;
				} catch (qsaError) {
				} finally {
					if (!old) {
						context.removeAttribute("id");
					}
				}
			}
		}
	}

	// All others
	//  Except for the above shortcuts and calls querySelectorAll Method directly get the result, the rest need to call select To get the results 
	/*
	 * rtrim = new RegExp("^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)"
	 *			+ whitespace + "+$", "g"),
	 * whitespace = "[\\x20\\t\\r\\n\\f]";
	 *  The above rtrim The purpose of a regular expression is to get rid of it selector White space on both sides by the white space character whitespace Variable definitions 
	 * rtrim And the effect of new RegExp("^" + whitespace + "+|" + whitespace + "+$", "g") similar 
	 */
	return select(selector.replace(rtrim, "$1"), context, results, seed);
}

Dear friends, if you think the writing is good, help me top 1, give some power, thank you!


Related articles: