在Sizzle函數中,若是能快速處理或者經過querySelector處理,那麼就使用它處理。不然使用select函數處理 。javascript
select = Sizzle.select = function (selector, context, results, seed) { var i, tokens, token, type, find, // 斷定是不是 pre-compiled 的選擇器 compiled = typeof selector === "function" && selector, // 這裏因爲compiled 是false ,因此先認爲selector 是字符串,進入tokenize函數 ,進入詞法分析 。 // 將 selector 分爲組並返回 match = !seed && tokenize((selector = compiled.selector || selector)); };
因此,這一節的主要內容是 tokenize 函數css
tokenize = Sizzle.tokenize = function (selector, parseOnly) { var matched, match, tokens, type, soFar, groups, preFilters, // 先查看是否有緩存 cached = tokenCache[selector + " "]; if (cached) { // 若是有緩存,就先從緩衝中取 。 return parseOnly ? 0 : cached.slice(0); } soFar = selector; // 下面對選擇器從左至右進行分析 groups = []; // 用 , 分割的組合選擇器,每一個選擇器都是一個組 。 preFilters = Expr.preFilter; // 過濾器 while (soFar) { // 第一個運行matched 爲undefined,必定爲假 。 // 105行,找出逗號 rcomma = new RegExp("^" + whitespace + "*," + whitespace + "*"), // 逗號用來分組,因此下面if 的邏輯主要是添加組 ,即group 。 if (!matched || (match = rcomma.exec(soFar))) { if (match) { soFar = soFar.slice(match[0].length) || soFar; } // 將一個數組push到組中 。 groups.push((tokens = [])); } matched = false; // 106 行 rcombinators = new RegExp("^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*"), // 這個正則表達式就是爲了找出 關係符 ">+~ " 。 // 在上面添加了組,把逗號已經去掉,下面就是逗號以後的標識符 ,首先 match = rcombinators.exec(soFar)) 斷定關係符號,可是第一次從組跳下,這裏確定爲false 。因此又跳轉到以後的if 。 if ((match = rcombinators.exec(soFar))) { matched = match.shift(); tokens.push({ value: matched, // Cast descendant combinators to space type: match[0].replace(rtrim, " ") }); soFar = soFar.slice(matched.length); } // 這裏主要斷定表示符是 id ,class 仍是 tag 。 // identifier = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", 這裏的 \\\\. 表示 在css中,能夠有轉義字符做爲標識符 。好比 \$,\& // 捕捉屬性選擇器,這個正則是最難的,不必定徹底理解。 // attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + //"*([*^$|!~]?=)" + whitespace + //"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + //"*\\]", // booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", // 處理各類僞類 。 // pseudos = ":(" + identifier + ")(?:\\((" + //"('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + //"((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + //".*" + //")\\)|)", // matchExpr = { // "ID": new RegExp("^#(" + identifier + ")"), // "CLASS": new RegExp("^\\.(" + identifier + ")"), // "TAG": new RegExp("^(" + identifier + "|[*])"), // "ATTR": new RegExp("^" + attributes), // "PSEUDO": new RegExp("^" + pseudos), // "CHILD": new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + // "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + // "*(\\d+)|))" + whitespace + "*\\)|)", "i"), // "bool": new RegExp("^(?:" + booleans + ")$", "i"), // "needsContext": new RegExp("^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + // whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i") // }, for (type in Expr.filter) { // 若是是上面的一種 、 // preFilters是用於分析選擇器的名字與參數 // 預處理,有的選擇器,好比屬性選擇器與僞類從選擇器組分割出來,還要再細分 // 屬性選擇器要切成屬性名,屬性值,操做符;僞類要切爲類型與傳參; // 子元素過濾僞類還要根據an+b的形式再劃分 if ((match = matchExpr[type].exec(soFar)) && (!preFilters[type] || (match = preFilters[type](match)))) { matched = match.shift(); tokens.push({ value: matched, type: type, matches: match }); soFar = soFar.slice(matched.length); } } if (!matched) { break; } } // 正常狀況下,soFar所有解析完畢,此時爲空字符串 。若是僅僅如parse,那麼返回剩下長度,不然,拋出異常 。 return parseOnly ? soFar.length : soFar ? Sizzle.error(selector) : // 緩存起來 。 tokenCache(selector, groups).slice(0); };
// 這是filter,返回match的柯里化函數 。在編譯部分會使用,這裏不會用到 。 filter: { // 標籤過濾器 ,返回一個柯里化函數 。 // 驗證元素的名稱是否就是當前傳入的Tag 。Tag放入閉包中 。 "TAG": function (nodeNameSelector) { var nodeName = nodeNameSelector.replace(runescape, funescape).toLowerCase(); return nodeNameSelector === "*" ? function () { return true; } : function (elem) { return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; }; }, // 類過濾器 ,返回一個柯里化函數 。 // 驗證元素的類名稱是否包含當前傳入的className 。 "CLASS": function (className) { var pattern = classCache[className + " "]; return pattern || (pattern = new RegExp("(^|" + whitespace + ")" + className + "(" + whitespace + "|$)")) && classCache(className, function (elem) { return pattern.test(typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || ""); }); }, "ATTR": function (name, operator, check) { // 返回的是函數 ! return function (elem) { var result = Sizzle.attr(elem, name); // 若是屬性值爲空 if (result == null) { return operator === "!="; } // 若是操做符爲空, if (!operator) { return true; } // 將屬性值轉化爲字符串。 result += ""; return operator === "=" ? result === check : operator === "!=" ? result !== check : operator === "^=" ? check && result.indexOf(check) === 0 : operator === "*=" ? check && result.indexOf(check) > -1 : operator === "$=" ? check && result.slice(-check.length) === check : operator === "~=" ? (" " + result.replace(rwhitespace, " ") + " ").indexOf(check) > -1 : operator === "|=" ? result === check || result.slice(0, check.length + 1) === check + "-" : false; }; }, // 這裏處理子元素過濾僞類,如:nth-child, :first-child, :only-child "CHILD": function (type, what, argument, first, last) { var simple = type.slice(0, 3) !== "nth", forward = type.slice(-4) !== "last", ofType = what === "of-type"; return first === 1 && last === 0 ? // Shortcut for :nth-*(n) function (elem) { return !!elem.parentNode; } : function (elem, context, xml) { var cache, outerCache, node, diff, nodeIndex, start, dir = simple !== forward ? "nextSibling" : "previousSibling", parent = elem.parentNode, name = ofType && elem.nodeName.toLowerCase(), useCache = !xml && !ofType; if (parent) { // :(first|last|only)-(child|of-type) if (simple) { while (dir) { node = elem; while ((node = node[dir])) { if (ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1) { return false; } } // Reverse direction for :only-* (if we haven't yet done so) start = dir = type === "only" && !start && "nextSibling"; } return true; } start = [forward ? parent.firstChild : parent.lastChild]; // non-xml :nth-child(...) stores cache data on `parent` if (forward && useCache) { // Seek `elem` from a previously-cached index outerCache = parent[expando] || (parent[expando] = {}); cache = outerCache[type] || []; nodeIndex = cache[0] === dirruns && cache[1]; diff = cache[0] === dirruns && cache[2]; node = nodeIndex && parent.childNodes[nodeIndex]; while ((node = ++nodeIndex && node && node[dir] || // Fallback to seeking `elem` from the start (diff = nodeIndex = 0) || start.pop())) { // When found, cache indexes on `parent` and break if (node.nodeType === 1 && ++diff && node === elem) { outerCache[type] = [dirruns, nodeIndex, diff]; break; } } // Use previously-cached element index if available } else if (useCache && (cache = (elem[expando] || (elem[expando] = {}))[type]) && cache[0] === dirruns) { diff = cache[1]; // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) } else { // Use the same loop as above to seek `elem` from the start while ((node = ++nodeIndex && node && node[dir] || (diff = nodeIndex = 0) || start.pop())) { if ((ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1) && ++diff) { // Cache the index of each encountered element if (useCache) { (node[expando] || (node[expando] = {}))[type] = [dirruns, diff]; } if (node === elem) { break; } } } } // Incorporate the offset, then check against cycle size diff -= last; return diff === first || (diff % first === 0 && diff / first >= 0); } }; }, "PSEUDO": function (pseudo, argument) { // pseudo-class names are case-insensitive // http://www.w3.org/TR/selectors/#pseudo-classes // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters // Remember that setFilters inherits from pseudos var args, fn = Expr.pseudos[pseudo] || Expr.setFilters[pseudo.toLowerCase()] || Sizzle.error("unsupported pseudo: " + pseudo); // The user may use createPseudo to indicate that // arguments are needed to create the filter function // just as Sizzle does if (fn[expando]) { return fn(argument); } // But maintain support for old signatures if (fn.length > 1) { args = [pseudo, pseudo, "", argument]; return Expr.setFilters.hasOwnProperty(pseudo.toLowerCase()) ? markFunction(function (seed, matches) { var idx, matched = fn(seed, argument), i = matched.length; while (i--) { idx = indexOf(seed, matched[i]); seed[idx] = !(matches[idx] = matched[i]); } }) : function (elem) { return fn(elem, 0, args); }; } return fn; } },
preFilter: { "ATTR": function (match) { match[1] = match[1].replace(runescape, funescape); // Move the given value to match[3] whether quoted or unquoted match[3] = (match[3] || match[4] || match[5] || "").replace(runescape, funescape); if (match[2] === "~=") { match[3] = " " + match[3] + " "; } return match.slice(0, 4); }, "CHILD": function (match) { /* matches from matchExpr["CHILD"] 1 type (only|nth|...) 2 what (child|of-type) 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) 4 xn-component of xn+y argument ([+-]?\d*n|) 5 sign of xn-component 6 x of xn-component 7 sign of y-component 8 y of y-component */ //將它的僞類名稱與傳參拆分爲更細的單元,以數組形式返回 //好比 ":nth-child(even)"變爲 //["nth","child","even", 2, 0, undefined, undefined, undefined] match[1] = match[1].toLowerCase(); if (match[1].slice(0, 3) === "nth") { // nth-* requires argument if (!match[3]) { Sizzle.error(match[0]); } // numeric x and y parameters for Expr.filter.CHILD // remember that false/true cast respectively to 0/1 match[4] = +(match[4] ? match[5] + (match[6] || 1) : 2 * (match[3] === "even" || match[3] === "odd")); match[5] = +((match[7] + match[8]) || match[3] === "odd"); // other types prohibit arguments } else if (match[3]) { Sizzle.error(match[0]); } return match; }, "PSEUDO": function (match) { //將它的僞類名稱與傳參進行再處理 //好比:contains僞類會去掉兩邊的引號,反義僞類括號部分會再次提取 var excess, unquoted = !match[6] && match[2]; if (matchExpr["CHILD"].test(match[0])) { return null; } // Accept quoted arguments as-is if (match[3]) { match[2] = match[4] || match[5] || ""; // Strip excess characters from unquoted arguments } else if (unquoted && rpseudo.test(unquoted) && // Get excess from tokenize (recursively) (excess = tokenize(unquoted, true)) && // advance to the next closing parenthesis (excess = unquoted.indexOf(")", unquoted.length - excess) - unquoted.length)) { // excess is a negative index match[0] = match[0].slice(0, excess); match[2] = unquoted.slice(0, excess); } // Return only captures needed by the pseudo filter method (type and argument) return match.slice(0, 3); } },