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