Detailed description of the sizzle selector in jQuery source code analysis, jquerysizzle
Preface
Sizzle was originally used as a DOM selector in jQuery. Later, it was separated by John Resig to become a separate project and can be directly imported into the project for use.
Click here: Jquery/sizzle.
Originally, we used jQuery as the selector and selected some # id or. class and useddocument.getElementById
Ordocument.getElemensByClassName
You can quickly lock the location of the DOM and return it to jQuery as an object. However, sometimes you may encounter complicated choices. div div.hot>span
This class of functions certainly won't work. The first consideration is Element.querySelectorAll()
Function, but this function has a serious compatibility problem MDN querySelectorAll. At this time, sizzle will be used.
The description of the init function is described as follows. If the find function is not introduced, the Sizzle function represents jQuery in essence.
There are two forms of this function in jQuery: one for the prototype and one for the attribute.jQuery.fn.find
:
JQuery. fn. find = function (selector) {var I, ret, len = this. length, self = this; // I really don't know what it is. if (typeof selector! = "String") {// fn. pushStack and jquery. merge is similar, but a jquery object is returned, and // jquery has a prevObject attribute pointing to its own return this. pushStack (jQuery (selector ). filter (function () {for (I = 0; I <len; I ++) {// jQuery. contains (a, B) determine whether a is the father of B if (jQuery. contains (self [I], this) {return true ;}});} ret = this. pushStack ([]); for (I = 0; I <len; I ++) {// reference jQuery here. find function jQuery. find (selector, self [I], re T);} // uniqueSort deduplication function return len> 1? JQuery. uniqueSort (ret): ret ;}
jQuery.fn.find
Generally$('.test').find("span")
This indicates $(‘.test')
, Understand this point, the following things will naturally understand.
ThenjQuery.find
Function, the focus of this chapter.
Let's first look at a regular expression:
var rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/;rquickExpr.exec('#id') //["#id", "id", undefined, undefined]rquickExpr.exec('div') //["div", undefined, "div", undefined]rquickExpr.exec('.test') //[".test", undefined, undefined, "test"]rquickExpr.exec('div p')// null
You may wonder that the rquickExpr name has already appeared once. In fact, Sizzle is a closure. The rquickExpr variable is in the Sizzle closure and does not affect the global jQuery. This regular expression is mainly used to differentiate tags, IDs, and classes, and has a certain rule from the returned array. This rule can be used to determine which selector is specific.
JQuery. find = Sizzle; function Sizzle (selector, context, results, seed) {var m, I, elem, nid, match, groups, newSelector, newContext = context & context. ownerDocument, // nodeType ults to 9, since context defaults to document nodeType = context? Context. nodeType: 9; results = results | []; // Return early from callwith invalid selector or context if (typeof selector! = "String" |! Selector | nodeType! = 1 & nodeType! = 9 & nodeType! = 11) {return results;} // Try to export cut find operations (as opposed to filters) in HTML documents if (! Seed) {if (context? Context. ownerDocument | context: preferredDoc )! = Document) {// The setDocument function is used to set context to document. Considering the browser compatibility, setDocument (context);} context = context | document; // true if (documentIsHTML) {// match is the regular array if (nodeType! = 11 & (match = rquickExpr.exe c (selector) {// if the selector is id (m = match [1]) {// Document context if (nodeType = 9) {if (elem = context. getElementById (m) {if (elem. id = m) {results. push (elem); return results ;}} else {return results ;}// non-document cases} else {if (newContext & (elem = newContext. getElementById (m) & contains (context, elem) & elem. id = m) {results. pu Sh (elem); return results ;}// selector is the tagName} else if (match [2]) {// here's push: var push = arr. push. apply (results, context. getElementsByTagName (selector); return results; // The selector class condition} else if (m = match [3]) & support. getElementsByClassName & context. getElementsByClassName) {push. apply (results, context. getElementsByClassName (m); return results ;}}// if the browser supports queryS ElectorAll if (support. qsa &&! CompilerCache [selector + ""] & (! RbuggyQSA |! RbuggyQSA. test (selector) {if (nodeType! = 1) {newContext = context; newSelector = selector; // qSA looks outside Element context, which is not what we want // Support: IE <= 8, still consider compatibility} else if (context. nodeName. toLowerCase ()! = "Object") {// Capture the context ID, setting it first if necessary if (nid = context. getAttribute ("id") {nid = nid. replace (rcssescape, fcssescape);} else {context. setAttribute ("id", (nid = expando);} // groups of Sizzle lexical analysis = tokenize (selector); I = groups. length; while (I --) {groups [I] = "#" + nid + "" + toSelector (groups [I]);} newSelector = groups. join (","); // Expand context for sibling selectors newContext = rsibling. test (selector) & testContext (context. parentNode) | context;} if (newSelector) {try {push. apply (results, newContext. querySelectorAll (newSelector); return results;} catch (qsaError) {} finally {if (nid = expando) {context. removeAttribute ("id") ;}}}}// All others, select function and tokenize function. Then return select (selector. replace (rtrim, "$1"), context, results, seed );}
The entire analysis process looks very long because of various factors, including efficiency and browser compatibility, but the logic is not difficult at all: first, determine whether the selector is a non-string, then, the regular rquickExpr matches the selector to obtain the array and considers the id, tagName, and class situations in sequence. These are all very simple and are a single choice. Generally, the getElement function provided by the browser can be used to solve this problem. A little complicated, such div div.show p
First, consider whether the querySelectorAll function is supported, and then consider browser compatibility with IE <8. If not, hand it over to the select function (next chapter ).
Advantages of Sizzle
Sizzle uses the right-to-left method, which is more efficient.
When processing html, the browser makes a DOM tree, parses the css, and then adds css and DOM tess to generate a render tree. The render tree is used for rendering, not one-to-one correspondence, as shown in figuredisplay:none
Will not appear in the render tree.
If the matching method is left to right,div div.show p
,
- Find the div node,
- Find the div and class show DOM from the subnode of 1. If no DOM is found, the previous step is returned.
- Locate the p element from the subnode 2. If no p element is found, the previous step is returned.
If one step cannot be found, go back up until you traverse all the Divs. The efficiency is very low.
If the method is from right to left,
- Match All p nodes first,
- Pay attention to the judgment of the result in 1. If the parent node appears in order
div.show
And div, it is retained; otherwise, it is discarded.
Because there can be several child nodes and only one parent node, the efficiency is very high from right to left.
Derivative Functions
JQuery. fn. pushStack
jQuery.fn.pushStack
Is similar jQuery.merge
It accepts a parameter, merges the parameter (array) into a jQuery object, and returns the result. The source code is as follows:
jQuery.fn.pushStack = function (elems) { // Build a new jQuery matched element set var ret = jQuery.merge(this.constructor(), elems); // Add the old object onto the stack (as a reference) ret.prevObject = this; // Return the newly-formed element set return ret;}
JQuery. contains
This function is used to determine whether the DOM is a parent-child relationship. The source code is as follows:
JQuery. contains = function (context, elem) {// considering compatibility, set the value of context if (context. ownerDocument | context )! = Document) {setDocument (context);} return contains (context, elem);} // contains is an internal function to determine whether DOM_a is DOM_ B's var contains = function (, b) {var adown =. nodeType = 9? A.doc umentElement: a, bup = B & B. parentNode; return a === bup | !! (Bup & bup. nodeType = 1 & (adown. contains? Adown. contains (bup): a. compareDocumentPosition & a. compareDocumentPosition (bup) & 16 ));}
JQuery. uniqueSort
JQuery's de-duplication function, but this de-duplication function processes DOM element arrays and cannot process string or number arrays to see what is special:
JQuery. uniqueSort = function (results) {var elem, duplicates = [], j = 0, I = 0; // hasDuplicate is a flag that determines whether the same element exists. hasDuplicate =! Support. detectDuplicates; sortInput =! Support. sortStable & results. slice (0); results. sort (sortOrder); if (hasDuplicate) {while (elem = results [I ++]) {if (elem = results [I]) {j = duplicates. push (I) ;}while (j --) {// splice is used to delete duplicate elements. splice (duplicates [j], 1) ;}// Clear input after sorting to release objects // See https://github.com/jquery/sizzle/pull/225 sortInput = null; return results ;}
The sortOrder function is as follows. You need to put the two functions together for better understanding:
Var sortOrder = function (a, B) {// indicates that the same element exists. Set flag to true if (a = B) {hasDuplicate = true; return 0 ;} // Sort on method existence if only one input has compareDocumentPosition var compare =! A. compareDocumentPosition -! B. compareDocumentPosition; if (compare) {return compare;} // Calculate position if both inputs belong to the same document compare = (. ownerDocument | a) === (B. ownerDocument | B )? A. compareDocumentPosition (B): // Otherwise we know they are disconnected 1; // Disconnected nodes if (compare & 1 | (! Support. sortDetached & B. compareDocumentPosition (a) === compare) {// Choose the first element that is related to our preferred document if (a === document |. ownerDocument === preferredDoc & contains (preferredDoc, a) {return-1;} if (B === document | B. ownerDocument === preferredDoc & contains (preferredDoc, B) {return 1;} // Maintain original order return sortInput? (IndexOf (sortInput, a)-indexOf (sortInput, B): 0;} return compare & 4? -1: 1 ;}
Summary
It can be said that it is a long journey to start Sizzle today! The tokens and select functions in Sizzle will be accepted below. If you are interested, you can continue to pay attention to the customer's home. I hope the content of this article will help you.