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!