jQuery selector source code interpretation (ii) : select method

  • 2020-05-24 05:09:49
  • OfStack


/*
 * select Method is Sizzle The core method of the selector package 1 , which mainly accomplish the following tasks: 
 * 1 , call tokenize Method completes the parsing of the selector 
 * 2 , for no initial set (i.e seed No assignment) and single 1 Block selector (that is, no comma in the selector string), 
 *   Complete the following: 
 *  1)  For the first selector ID The type and context is document , then get the object directly instead of the incoming one context object 
 *  2)  If the selector is single 1 Selector, and yes id , class , tag Type, get the match directly and return it DOM The element 
 *  3)  To obtain the final 1 a id , class , tag Match the type selector DOM The element is assigned to the initial collection (i.e seed Variables) 
 * 3 , by calling compile Method gets "precompiled" code and executes it, getting and returning a match DOM The element 
 * 
 * @param selector  The empty selector string has been removed 
 * @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 select(selector, context, results, seed) {
	
	var i, tokens, token, type, find, 
	//  call tokenize Function analysis selector
	match = tokenize(selector);

	//  If no initial set is provided 
	if (!seed) {
		// Try to minimize operations if there is only one group
		//  If only 1 A group selector, that is, a selector string without commas 
		if (match.length === 1) {
			// Take a shortcut and set the context if the root selector
			// is an ID
			/*
			 *  The following code is used to handle the root selector is ID Type shortcut 
			 * 
			 *  In this use slice[0] To create a 1 A new set, 
			 *  Make sure that the original collection is not changed later in the code 
			 */
			tokens = match[0] = match[0].slice(0);
			/*
			 *  If the selector is id The type starts and starts 2 A is a relation character (i.e +~> Or space), 
			 *  You get id Belonging object as context Continue to complete subsequent matches 
			 * 
			 *  The conditional judgment here is in order: 
			 * tokens.length > 2  If: tokens There are more than two selectors 
			 * (token = tokens[0]).type === "ID"  : the first 1 The selector types are ID (that is, to # At the beginning of), 
			 * support.getById  Support: getElementById function 
			 * context.nodeType === 9  : context The object is document
			 * documentIsHTML  : currently dealing with HTML code 
			 * Expr.relative[tokens[1].type]  : the first 2 a tokens The element is 1 Relationship (i.e +~> Or Spaces) 
			 *  When all of the above conditions are met, execute if Inside the body of a statement 
			 */
			if (tokens.length > 2 && (token = tokens[0]).type === "ID"
					&& support.getById && context.nodeType === 9
					&& documentIsHTML && Expr.relative[tokens[1].type]) {

				//  Points the current context to 1 a ID The node object specified by the selector 
				context = (Expr.find["ID"](token.matches[0].replace(
						runescape, funescape), context) || [])[0];
				
				//  If not specified in the current context ID Object, returns directly results
				if (!context) {
					return results;
				}
				
				//  The selector string drops the number 1 a ID The selector 
				selector = selector.slice(tokens.shift().value.length);
			}

			// Fetch a seed set for right-to-left matching
			/* 
			 *  The following while The function of the loop is used according to the last 1 a id , class , tag The selector of the type gets the initial collection 
			 *  A simple example: if the selector is "div[title='2']" . 
			 *  According to the code div Get all of them context Under the div Node, and assign this set to seed Variables, 
			 *  And then in the call compile Function that generates precompiled code, 
			 *  The precompiled code is executed in the initial collection above [title='2'] The matching of 
			 * 
			 *  First, check the selector string for the presence of and needsContext A character that matches a regular expression 
			 *  If not, it will be filtered from right to left according to the selector DOM node 
			 *  Otherwise, the call will be executed after Mr. Precompiled into the code compile Methods).  
			 */
			
			/*
			 * "needsContext" : new RegExp("^" + whitespace
			 *		+ "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("
			 *		+ whitespace + "*((?:-\\d)?\\d*)" + whitespace
			 *		+ "*\\)|)(?=[^-]|$)", "i")
			 * needsContext Matches whether the selector string contains the following: 
			 * 1 , >+~3 Kind of relationship between operator 
			 * 2 , :even , :odd , :eq , :gt , :lt , :nth , :first , :last8 A pseudo class 
			 *  Among them, (?=[^-]|$) To filter out something like :first-child Such as with the middle bar and above 8 Other selectors at the beginning of a word 
			 */
			i = matchExpr["needsContext"].test(selector) ? 0
					: tokens.length;
			while (i--) {
				token = tokens[i];

				// Abort if we hit a combinator
				//  A relation character is encountered to break out of the loop 
				if (Expr.relative[(type = token.type)]) {
					break;
				}
				if ((find = Expr.find[type])) {
					// Search, expanding context for leading sibling
					// combinators
					/*
					 * rsibling = new RegExp(whitespace + "*[+~]")
					 * rsibling Used to determine token Is the selector a sibling 
					 */
					if ((seed = find(token.matches[0].replace(
							runescape, funescape), rsibling
							.test(tokens[0].type)
							&& context.parentNode || context))) {

						// If seed is empty or no tokens remain, we can
						// return early
						//  Remove the selector you just used 
						tokens.splice(i, 1);
						selector = seed.length && toSelector(tokens);
						/*
						 *  if selector Is empty, indicating that the selector is only single 1id , class , tag The type, 
						 *  Therefore, directly return the obtained results, otherwise, in the fetch seed On the basis of continued matching 
						 */
						if (!selector) {
							push.apply(results, seed);
							return results;
						}

						break;
					}
				}
			}
		}
	}

	// Compile and execute a filtering function
	// Provide `match` to avoid retokenization if we modified the
	// selector above
	/*
	 *  To perform first compile(selector, match) It will return 1 "Precompiled" functions, 
	 *  The function is then called to get the final match 
	 */
	compile(selector, match)(seed, context, !documentIsHTML, results,
			rsibling.test(selector));
	return results;
}


Related articles: