三、Sizzle选择器
Sizele是一款纯JavaScript实现的CSS选择器引擎。
CSS选择器匹配的两种实现思路:例如要选择(div.main > p)
- 从左向右:先查找div.main在查找p。
- 从右向左:先查找p在检查每个元素的父元素是否是div.main。
总体分为3部分:
- 处理选择器表达式:解析选择器表达式中的元素表达式(例如上述的:div.main、p)和关系表达式(>)。
- 处理元素表达式:用元素表达式的一部分的查找(div),另一部分进行匹配(.main)。
- 处理关系表达式:按元素表达符查找(>),用块表达式(p)对查询结果进行筛选。
查询思路比较:
- 从左到右查找思路:不断的缩小查找范围。
- 从右到左查找思路:先查找后过滤。
- 两者比较:从左到右的查找过程中,每次处理元素表达式的时候都要处理其未知数量的子元素或后代元素,而从右到左的查找过程中,只要处理单个的父元素或有限数量的祖先元素,因此在大多数情况下,从右到左的查询方式的效果高于从左到右查询。
Sizzle函数结构
Sizzle
Sizzle函数解析:
/* Sizele是一款纯JavaScript实现的CSS选择器引擎。 CSS选择器匹配的两种实现思路:例如要选择(div.main > p) 从左向右:先查找div.main在查找p。 从右向左:先查找p在检查每个元素的父元素是否是div.main。 总体分为3部分: 处理选择器表达式:解析选择器表达式中的元素表达式(例如上述的:div.main、p)和关系表达式(>)。 处理元素表达式:用元素表达式的一部分的查找(div),另一部分进行匹配(.main)。 处理关系表达式:按元素表达符查找(>),用块表达式(p)对查询结果进行筛选。 查询思路比较: 从左到右查找思路:不断的缩小查找范围。 从右到左查找思路:先查找后过滤。 两者比较:从左到右的查找过程中,每次处理元素表达式的时候都要处理其未知数量的子元素或后代元素,而从右到左的查找过程中,只要处理单个的父元素或有限数量的祖先元素,因此在大多数情况下,从右到左的查询方式的效果高于从左到右查询。 */ // 代码行499——2765 var Sizzle = /*! * Sizzle CSS Selector Engine v2.3.3 * https://sizzlejs.com/ * * Copyright jQuery Foundation and other contributors * Released under the MIT license * http://jquery.org/license * * Date: 2016-08-08 */ (function (window) { var i, support, Expr, getText, isXML, tokenize, compile, select, outermostContext, sortInput, hasDuplicate, // Local document vars // 本地文档变量 setDocument, document, docElem, documentIsHTML, rbuggyQSA, rbuggyMatches, matches, contains, // Instance-specific data // 特定数据 expando = "sizzle" + 1 * new Date(), // 唯一标识符,通过sizzle+随机数。 // 每个载入浏览器的 HTML 文档都会成为 Document 对象。 // Document 对象使我们可以从脚本中对 HTML 页面中的所有元素进行访问。 // 提示:Document 对象是 Window 对象的一部分,可通过 window.document 属性对其进行访问。 preferredDoc = window.document, dirruns = 0, done = 0, classCache = createCache(), tokenCache = createCache(), compilerCache = createCache(), // 函数sortOrder(a, b)负责比较元素a和元素b在文档中的位置。 sortOrder = function (a, b) { // 如果a全等于b则设置hasDuplicate(中文直译,存在重复)为true,表示存在重复元素。 if (a === b) { hasDuplicate = true; } // 且返回0 return 0; }, // Instance methods // 实例方法 hasOwn = ({}).hasOwnProperty, // hasOwn查看是否是对象自身下面的属性 arr = [], // arr 定义一个空数组 pop = arr.pop, // pop 用于删除并返回数组的最后一个元素。 push_native = arr.push, // push_native 可向数组的末尾添加一个或多个元素,并返回新的长度。 push = arr.push, // push可向数组的末尾添加一个或多个元素,并返回新的长度。 slice = arr.slice, // slice() 方法可从已有的数组中返回选定的元素 // Use a stripped-down indexOf as it's faster than native // https://jsperf.com/thor-indexof-vs-for/5 // 使用精简indexOf,因为它比本机更快。用于判定元素是否在此数组内,存在返回当前元素的下标,否则返回-1 indexOf = function (list, elem) { var i = 0, len = list.length; for (; i < len; i++) { if (list[i] === elem) { return i; } } return -1; }, // 布尔值:可以设置布尔值的属性汇总 booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", // Regular expressions // 正则表达式 // 空白的正则 // http://www.w3.org/TR/css3-selectors/#whitespace whitespace = "[\\x20\\t\\r\\n\\f]", // "\\."转义反斜杠+任意字符,“[\\w-]”匹配包括下划线的任何单词字符,“[^\0-\\xa0]”,下划线连字符 // ID选择器 // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+", // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors // 属性选择器 attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + // Operator (capture 2) "*([*^$|!~]?=)" + whitespace + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + "*\\]", // 伪类的正则 pseudos = ":(" + identifier + ")(?:\\((" + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: // 若要减少预过滤器中需要标记的选择器数量,请选择参数 // 1. quoted (capture 3; capture 4 or capture 5) // 引用 "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + // 2. simple (capture 6) // 面向对象类 "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + // 3. anything else (capture 2) // 其他 ".*" + ")\\)|)", // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter // 前导和非转义的尾随空格,捕获后一个空格之前的一些非空格字符 // 紧邻元素选择器的正则 rwhitespace = new RegExp(whitespace + "+", "g"), // 去掉两端空白正则 rtrim = new RegExp("^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g"), // 并联选择器的正则 rcomma = new RegExp("^" + whitespace + "*," + whitespace + "*"), // 关系选择器的正则 rcombinators = new RegExp("^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*"), // Attribute 对象引用 rattributeQuotes = new RegExp("=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g"), rpseudo = new RegExp(pseudos), ridentifier = new RegExp("^" + identifier + "$"), matchExpr = { //匹配简单表达式“#id”,并解析“#”之后的字符串,其中含有一个分组:id "ID": new RegExp("^#(" + identifier + ")"), //匹配简单表达式“.class”,并解析“.”之后的字符串,其中含有一个分组:类样式 "CLASS": new RegExp("^\\.(" + identifier + ")"), //匹配简单表达式“tag”,并解析标签名,其中含有一个分组:标签名 "TAG": new RegExp("^(" + identifier + "|[*])"), //匹配属性表达式“[attribute = "value"]”,并解析属性名和属性值,其中包含5个分组:属性名、等号部分、引号、属性值、无引号时的属性值 "ATTR": new RegExp("^" + attributes), //匹配伪类表达式,并解析“:”之后的伪类和伪类参数,其中包含3个分组:伪类、引号、伪类参数 "PSEUDO": new RegExp("^" + pseudos), // 匹配子元素伪类表达式:nth-child(index/even/odd/equation)、:first-child、:last-child、:only-child,并解析子元素伪类和伪类参数,其中包含2个分组:子元素伪类、伪类参数 "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"), // For use in libraries implementing .is() // We use this for POS matching in `select` // 位置伪类 //匹配上下文 "needsContext": new RegExp("^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i") }, // jQuery自定义的input伪类 rinputs = /^(?:input|select|textarea|button)$/i, // jQuery自定义的header伪类 rheader = /^h\d$/i, // 取函授的toString判定是否原生API,比如一些库伪造了getElementsByClassName rnative = /^[^{]+\{\s*\[native \w/, // Easily-parseable/retrievable ID or TAG or CLASS selectors // 判定选择符是否为单个ID选择器,或标签选择器或类选择器 rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, // 匹配+~两个字符 rsibling = /[+~]/, // CSS escapes // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters // CSS 字符转义 runescape = new RegExp("\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig"), funescape = function (_, escaped, escapedWhitespace) { var high = "0x" + escaped - 0x10000; // NaN means non-codepoint // NaN是非代码点 // Support: Firefox<24 // Workaround erroneous numeric interpretation of +"0x" // 解决0x的数值解释的错误 return high !== high || escapedWhitespace ? escaped : high < 0 ? // BMP codepoint // BMP 代码点 // 将 Unicode 编码转为一个字符 String.fromCharCode(high + 0x10000) : // Supplemental Plane codepoint (surrogate pair) // 补充平面码点(代理对) String.fromCharCode(high >> 10 | 0xD800, high & 0x3FF | 0xDC00); }, // CSS string/identifier serialization // https://drafts.csswg.org/cssom/#common-serializing-idioms // CSS字符串/标识符序列化 rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, fcssescape = function (ch, asCodePoint) { if (asCodePoint) { // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER // U+0000 NULL变成U+FFFD替换字符 if (ch === "\0") { return "\uFFFD"; } // Control characters and (dependent upon position) numbers get escaped as code points // 控制字符和(取决于位置)数字被转义为代码点 return ch.slice(0, -1) + "\\" + ch.charCodeAt(ch.length - 1).toString(16) + " "; } // Other potentially-special ASCII characters get backslash-escaped // 其他可能特殊的ASCII字符采用反斜杠转义 return "\\" + ch; }, // Used for iframes // See setDocument() // Removing the function wrapper causes a "Permission Denied" // error in IE //用于在IE,iframe的setDocument()删除函数包装器会导致“权限被拒绝” unloadHandler = function () { setDocument(); }, // 禁用祖先元素 disabledAncestor = addCombinator( function (elem) { return elem.disabled === true && ("form" in elem || "label" in elem); }, { dir: "parentNode", next: "legend" } ); // Optimize for push.apply( _, NodeList ) // 将一个NodeList转换为一个纯数组 try { push.apply( (arr = slice.call(preferredDoc.childNodes)), preferredDoc.childNodes ); // Support: Android<4.0 // Detect silently failing push.apply // 检测push.apply的失败 arr[preferredDoc.childNodes.length].nodeType; } catch (e) { push = { apply: arr.length ? // Leverage slice if possible // 尽可能使用silce function (target, els) { push_native.apply(target, slice.call(els)); } : // Support: IE<9 // Otherwise append directly // 否则直接添加 function (target, els) { var j = target.length, i = 0; // Can't trust NodeList.length // 不依赖NodeList.length的可靠性 while ((target[j++] = els[i++])) { } target.length = j - 1; } }; } // 选择器引擎入口,查找与选择器表达式,selector匹配的元素集合: //Sizzle主函数被设计得能递归自身,参数依次是选择符,上下文对象,结果集,种子集 function Sizzle(selector, context, results, seed) { // selector: CSS选择器表达式 // context: DOM元素或文档对象,作为查找元素的上下文,用于限定查找范围。 // results: 可选的数组或类数组,将把要查找的元素添加到其中。 // seed: 可选的元素集合,将从该元素集合中过滤出匹配选择器表达式的元素集合。 // 除了第一个参数,其他都是可选,上下文对象默认是当前文档对象 // 定义局部变量 var m, // m: 用于存放每次匹配选择器表达式selector的结果。 i, elem, nid, match, groups, newSelector, // ownerDocument属性以Document对象的形式返回节点的 owner document。 // 在HTML中,HTML文档本身始终是元素的 ownerDocument。 newContext = context && context.ownerDocument, // nodeType defaults to 9, since context defaults to document // nodeType默认值为9,因为上下文默认值为document nodeType = context ? context.nodeType : 9; // 修正参数results,如果没有传入results参数,则默认为空数组[]。方法.find(selector)调用Sizzle()时会传入一个jQuery对象,匹配元素将会被添加到传入的jQuery对象中。 results = results || []; // Return early from calls with invalid selector or context // 如果参数selector是空字符串,或则不是字符串,nodeType不是1或9或11,则忽略本次查询,直接返回传入的参数results。 if (typeof selector !== "string" || !selector || nodeType !== 1 && nodeType !== 9 && nodeType !== 11) { return results; } // Try to shortcut find operations (as opposed to filters) in HTML documents //尝试在HTML文档中快捷查找操作(而不是筛选) // 如果没有可选的元素集合 if (!seed) { // 判断context是否存在,存在(调用context的根节点或context节点)否则调用默认文档对象与Document对象进行对比,如果不是document对象的话调用setDocument(context)进行设置document对象。 if ((context ? context.ownerDocument || context : preferredDoc) !== document) { setDocument(context); } // 如果未传入参数context,则默认为当前document对象。 context = context || document; if (documentIsHTML) { // If the selector is sufficiently simple, try using a "get*By*" DOM method // (excepting DocumentFragment context, where the methods don't exist) // 如果选择器足够简单,请尝试使用“get*By*”DOM方法 // (除了DocumentFragment context,那里不存在这些方法) if (nodeType !== 11 && (match = rquickExpr.exec(selector))) { // ID selector // ID 选择器 //如果只有一个ID,那么直接getElementById,然后判定其是否在DOM树 // 元素的ID确实为目标值就将它合并到结果集中 if ((m = match[1])) { // Document context // 代表整个文档(DOM 树的根节点)。 if (nodeType === 9) { // 如果context中能通过getElementById获取相关元素,赋值给elem if ((elem = context.getElementById(m))) { // Support: IE, Opera, Webkit // TODO: identify versions // getElementById can match elements by name instead of ID // 由于getElementById可以通过name来匹配元素,所以需要判断elem.id是否是真正需要匹配的元素id if (elem.id === m) { // 把elem添加到results中,并返回 results.push(elem); return results; } // 否则直接返回results } else { return results; } // Element context // 否则认为是元素 } else { // Support: IE, Opera, Webkit // TODO: identify versions // getElementById can match elements by name instead of ID // 如果newContext存在,且能够通过getElementById(m)获得元素节点,且节点id与待查找id相等,且context包含了元素节点。把elem添加到results中并返回results。 if (newContext && (elem = newContext.getElementById(m)) && contains(context, elem) && elem.id === m) { results.push(elem); return results; } } // Type selector //如果选择符为一个标签选择器 } else if (match[2]) { push.apply(results, context.getElementsByTagName(selector)); return results; // Class selector // 如果选择符为一个类选择器,浏览器又支持getElementsByClassName } else if ((m = match[3]) && support.getElementsByClassName && context.getElementsByClassName) { push.apply(results, context.getElementsByClassName(m)); return results; } } // Take advantage of querySelectorAll //尝试使用querySelectorAll,并且选择符不存在那些有问题的选择器 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 // Thanks to Andrew Dupont for this workaround technique // Support: IE <=8 // Exclude object elements } else if (context.nodeName.toLowerCase() !== "object") { // Capture the context ID, setting it first if necessary // 捕获上下文ID,如果需要,首先设置它 if ((nid = context.getAttribute("id"))) { nid = nid.replace(rcssescape, fcssescape); } else { context.setAttribute("id", (nid = expando)); } // Prefix every selector in the list // 给列表中的每个选择器加上前缀 groups = 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 // 否则去掉两边的空白开始查找 return select(selector.replace(rtrim, "$1"), context, results, seed); } /** * Create key-value caches of limited size * @returns {function(string, object)} Returns the Object data after storing it on itself with * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) * deleting the oldest entry */ // 创建一个缓存函数,它以自己为储存仓库,键名放到闭包内的一个数组内,当数组的个数超过Expr.cacheLength时,则去掉最前面的键值 function createCache() { var keys = []; // 定义cache函数,并返回 function cache(key, value) { // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) // 对键名进行改造,防止与Object.prototype的原生方法重名,比如toString, valueOf th native prototype properties // 通过push向keys数组中添加"key "并把返回的keys数组长度,与Expr.cacheLength(默认为50)进行对比, if (keys.push(key + " ") > Expr.cacheLength) { // Only keep the most recent entries // 把在cache中keys第一个元素相匹配的键值对删除,只保留最近的条目 delete cache[keys.shift()]; } // 返回一个cache对象,并添加键值对'key ':value。 return (cache[key + " "] = value); } return cache; } /** * Mark a function for special use by Sizzle * @param {Function} fn The function to mark */ // 用Sizzle标出特殊用途的函数 function markFunction(fn) { fn[expando] = true; return fn; } /** * Support testing using an element * @param {Function} fn Passed the created element and returns a boolean result */ // 用于做各种检测,比如是否支持某个API,API支持是否完美 function assert(fn) { // 创建fieldset标签,用于给form表单分组 var el = document.createElement("fieldset"); /* try/catch/finally 语句用于处理代码中可能出现的错误信息。错误可能是语法错误,通常是程序员造成的编码错误或错别字。也可能是拼写错误或语言中缺少的功能(可能由于浏览器差异)。 try语句允许我们定义在执行时进行错误测试的代码块。 catch 语句允许我们定义当 try 代码块发生错误时,所执行的代码块。 finally 语句在 try 和 catch 之后无论有无异常都会执行。 注意: catch 和 finally 语句都是可选的,但你在使用 try 语句时必须至少使用一个。 提示: 当错误发生时, JavaScript 会停止执行,并生成一个错误信息。使用 throw 语句 来创建自定义消息(抛出异常)。如果你将 throw 和 try 、 catch一起使用,就可以控制程序输出的错误信息。 */ try { return !!fn(el); } catch (e) { return false; } finally { // Remove from its parent by default // 默认情况下从其父节点删除生成的fieldset元素 if (el.parentNode) { el.parentNode.removeChild(el); } // release memory in IE // 释放IE内存 el = null; } } /** * Adds the same handler for all of the specified attrs * @param {String} attrs Pipe-separated list of attributes * @param {Function} handler The method that will be applied */ // 为所有指定的属性添加相同的处理程序 function addHandle(attrs, handler) { // attrs 类型为"|"分割的string // handler 类型为将被应用的function var arr = attrs.split("|"), i = arr.length; // 通过while循环给Expr.attrHandle添加键值对。 while (i--) { Expr.attrHandle[arr[i]] = handler; } } /** * Checks document order of two siblings * @param {Element} a * @param {Element} b * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b */ // 检查两个兄弟姐妹的文档顺序 // @returns {Number}如果a在b之前返回小于0,如果a在b之后返回大于0 function siblingCheck(a, b) { var cur = b && a, /* ||短路表达式 var foo = a || b; // 相当于 if(a){ foo = a; }else{ foo = b; } &&短路表达式 var bar = a && b; // 相当于 if(a){ bar = b; }else{ bar = a; } 多重短路表达式和简单短路表达式其实一样,只需要先把后面的当成一个整体,依次推进,得出最终值。 var foo = a && b && c // 相当于 a && (b && c) 1、在 Javascript 的逻辑运算中,0、""、null、false、undefined、NaN 都会判定为 false ,而其他都为 true ; 2、因为 Javascript 的内置弱类型域 (weak-typing domain),所以对严格的输入验证这一点不太在意,即便使用 && 或者 || 运算符的运算数不是布尔值,仍然可以将它看作布尔运算。虽然如此,还是建议如下: if(foo){ ... } //不够严谨 if(!!foo){ ... } //更为严谨,!!可将其他类型的值转换为boolean类型 */ diff = cur && a.nodeType === 1 && b.nodeType === 1 && a.sourceIndex - b.sourceIndex; /* diff布尔值,全部满足以下条件为true,否则为false 1、cur存在即a存在。 2、a,b同时都是元素 3、能使用IE sourceIndex 获得a,b两个元素的下标,且a的下标大于b的下标,即b在a的前面。 */ // Use IE sourceIndex if available on both nodes // 如果满足上述条件则 返回true(或1) if (diff) { return diff; } // Check if b follows a // 检查b是否跟在a后面 // 如果cur存在即a存在,进行while循环=》如果cur有下一个兄弟节点,则把兄弟节点赋值给cur。并判断cur是否全等于b节点,如果相等返回-1,否则继续while循环。 if (cur) { while ((cur = cur.nextSibling)) { if (cur === b) { return -1; } } } // 检查a是否存在a存在返回1否则返回-1 return a ? 1 : -1; } /** * Returns a function to use in pseudos for input types * @param {String} type */ // 创建一个伪类得过滤函数,此方法是根据表达元素得type值生成 // 例如::radio,:text,:checkbox,:file,:image等自定义伪类 function createInputPseudo(type) { // type 类型为字符串 return function (elem) { // 将elem的节点名转换成小写 var name = elem.nodeName.toLowerCase(); // 返回判断节点名是input,且节点类型与参数type相等的结果 return name === "input" && elem.type === type; }; } /** * Returns a function to use in pseudos for buttons * @param {String} type */ // 创建一个伪类得过滤函数,此方法是根据表达元素得type值或标签类型生成 // 例如::button, :submit自定义伪类 function createButtonPseudo(type) { return function (elem) { // 将元素标签名转换为小写并赋值给name var name = elem.nodeName.toLowerCase(); // 注意按钮有两种,input和button return (name === "input" || name === "button") && elem.type === type; }; } /** * Returns a function to use in pseudos for :enabled/:disabled * @param {Boolean} disabled true for :disabled; false for :enabled */ //返回一个函数,用于:enabled/:disabled在伪代码中使用 function createDisabledPseudo(disabled) { // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable return function (elem) { // 只有某些元素可以匹配:enabled或:disabled // Only certain elements can match :enabled or :disabled // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled if ("form" in elem) { // Check for inherited disabledness on relevant non-disabled elements: //检查为继承了disabledness有关非禁用元素: // * listed form-associated elements in a disabled fieldset // https://html.spec.whatwg.org/multipage/forms.html#category-listed // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled // * option elements in a disabled optgroup // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled // All such elements have a "form" property. // *在禁用的字段集中列出与表单相关的元素 // *禁用optgroup中的选项元素 //所有这些元素都有一个“form”属性。 if (elem.parentNode && elem.disabled === false) { // Option elements defer to a parent optgroup if present // 如果存在,选项元素遵从父optgroup if ("label" in elem) { if ("label" in elem.parentNode) { return elem.parentNode.disabled === disabled; } else { return elem.disabled === disabled; } } // Support: IE 6 - 11 // Use the isDisabled shortcut property to check for disabled fieldset ancestors // 使用isDisabled属性检查禁用自定义字段的祖先 return elem.isDisabled === disabled || // Where there is no isDisabled, check manually // 如果没有禁用isDisabled,请手动检查 /* jshint -W018 */ elem.isDisabled !== !disabled && disabledAncestor(elem) === disabled; } return elem.disabled === disabled; // Try to winnow out elements that can't be disabled before trusting the disabled property. // Some victims get caught in our net (label, legend, menu, track), but it shouldn't // even exist on them, let alone have a boolean value. //在信任disabled property之前,尝试筛选出不能禁用的元素。 } else if ("label" in elem) { return elem.disabled === disabled; } // Remaining elements are neither :enabled nor :disabled // 其余元素既不是:enabled,也不是:disabled return false; }; } /** * Returns a function to use in pseudos for positionals * @param {Function} fn */ // 用于创建位置伪类得过滤函数,他们是模拟从左到右得顺序进行选择 // 匹配到它时的结果集的位置来挑选元素的 // 例如::odd, :even, :eq, :gt, :lt, :first, :last function createPositionalPseudo(fn) { return markFunction(function (argument) { argument = +argument; // 标记匹配器为类似伪类分割器生成的匹配器 return markFunction(function (seed, matches) { var j, matchIndexes = fn([], seed.length, argument), i = matchIndexes.length; // Match elements found at the specified indexes // 匹配指定下标的元素 while (i--) { if (seed[(j = matchIndexes[i])]) { seed[j] = !(matches[j] = seed[j]); } } }); }); } /** * Checks a node for validity as a Sizzle context * @param {Element|Object=} context * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value */ // 检查节点作为Sizzle上下文的有效性 function testContext(context) { // 如果context存在且标签名存在,则返回context return context && typeof context.getElementsByTagName !== "undefined" && context; } // Expose support vars for convenience // 公开变量,以便后续使用 support = Sizzle.support = {}; /** * Detects XML nodes * @param {Element|Object} elem An element or a document * @returns {Boolean} True iff elem is a non-HTML XML node */ // 判断DOM节点是否位于XML文档中,或者其本身就是XML文档 isXML = Sizzle.isXML = function (elem) { // documentElement is verified for cases where it doesn't yet exist // (such as loading iframes in IE - #4833) // 如果elem不存在,则直接赋值给documentElement。否则把elem根节点赋值给documentElement。 var documentElement = elem && (elem.ownerDocument || elem).documentElement; // 如果documentElemnt存在且节点名称不是html返回true,否则返回false。 return documentElement ? documentElement.nodeName !== "HTML" : false; }; /** * Sets document-related variables once based on the current document * @param {Element|Object} [doc] An element or document object to use to set the document * @returns {Object} Returns the current document */ // 根据当前文档设置与文档相关的变量 setDocument = Sizzle.setDocument = function (node) { var hasCompare, subWindow, doc = node ? node.ownerDocument || node : preferredDoc; // Return early if doc is invalid or already selected // 如果doc等于document或doc不是DOM 树的根节点或没有返回html dom中的root节点 直接返回document // 如果文档对象等于当前文档对象,无法确认其文档对象,或没有HTML,直接返回,此情况出现机率为零 if (doc === document || doc.nodeType !== 9 || !doc.documentElement) { return document; } // Update global variables // 更新全局变量 document = doc; docElem = document.documentElement; // 是否为HTML文档,documentIsHTML用来声明文档类型为html documentIsHTML = !isXML(document); // Support: IE 9-11, Edge // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) // 卸载抛出“拒绝权限”错误后访问iframe文档 if (preferredDoc !== document && (subWindow = document.defaultView) && subWindow.top !== subWindow) { // Support: IE 11, Edge if (subWindow.addEventListener) { subWindow.addEventListener("unload", unloadHandler, false); // Support: IE 9 - 10 Only } else if (subWindow.attachEvent) { subWindow.attachEvent("onunload", unloadHandler); } } /* Attributes属性 ---------------------------------------------------------------------- */ // Support: IE<8 // Verify that getAttribute really returns attributes and not properties // 验证getAttribute确实返回属性而不是属性 // (excepting IE8 booleans) support.attributes = assert(function (el) { el.className = "i"; return !el.getAttribute("className"); }); /* getElement(s)By* 通过*获取元素 ---------------------------------------------------------------------- */ // Check if getElementsByTagName("*") returns only elements // 检查getElementsByTagName("*")是否只返回元素节点 support.getElementsByTagName = assert(function (el) { el.appendChild(document.createComment("")); return !el.getElementsByTagName("*").length; }); // Support: IE<9 // 判定getElementsByClassName值得信任 support.getElementsByClassName = rnative.test(document.getElementsByClassName); // Support: IE<10 // Check if getElementById returns elements by name // The broken getElementById methods don't pick up programmatically-set names, // so use a roundabout getElementsByName test //检查getElementById是否按名称返回元素 //被破坏的getElementById方法不会选择程序集名称, //所以使用一个迂回的getElementsByName测试 support.getById = assert(function (el) { docElem.appendChild(el).id = expando; return !document.getElementsByName || !document.getElementsByName(expando).length; }); // ID filter and find // ID筛选并找到 if (support.getById) { Expr.filter["ID"] = function (id) { var attrId = id.replace(runescape, funescape); return function (elem) { return elem.getAttribute("id") === attrId; }; }; // 给Expr添加find.id方法: // getElementById(id);查找拥有指定id的第一个元素 Expr.find["ID"] = function (id, context) { // 检查上下文是否支持原生方法,判断是否存在id且文档类型为html if (typeof context.getElementById !== "undefined" && documentIsHTML) { var elem = context.getElementById(id); return elem ? [elem] : []; } }; } else { Expr.filter["ID"] = function (id) { var attrId = id.replace(runescape, funescape); return function (elem) { var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); return node && node.value === attrId; }; }; // Support: IE 6 - 7 only // getElementById is not reliable as a find shortcut // getElementById() 查找拥有指定id得第一个元素 Expr.find["ID"] = function (id, context) { if (typeof context.getElementById !== "undefined" && documentIsHTML) { var node, i, elems, elem = context.getElementById(id); if (elem) { // Verify the id attribute node = elem.getAttributeNode("id"); if (node && node.value === id) { return [elem]; } // Fall back on getElementsByName elems = context.getElementsByName(id); i = 0; while ((elem = elems[i++])) { node = elem.getAttributeNode("id"); if (node && node.value === id) { return [elem]; } } } return []; } }; } // Tag // getElementsByTagName() 查找拥有指定标签名的元素集合 Expr.find["TAG"] = support.getElementsByTagName ? function (tag, context) { if (typeof context.getElementsByTagName !== "undefined") { return context.getElementsByTagName(tag); // DocumentFragment nodes don't have gEBTN } else if (support.qsa) { return context.querySelectorAll(tag); } } : function (tag, context) { var elem, tmp = [], i = 0, // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too results = context.getElementsByTagName(tag); // Filter out possible comments if (tag === "*") { while ((elem = results[i++])) { if (elem.nodeType === 1) { tmp.push(elem); } } return tmp; } return results; }; // Class // getElementsByClassName() 查获拥有指定类样式得元素集合 Expr.find["CLASS"] = support.getElementsByClassName && function (className, context) { if (typeof context.getElementsByClassName !== "undefined" && documentIsHTML) { return context.getElementsByClassName(className); } }; /* QSA/matchesSelector 匹配选择器 ---------------------------------------------------------------------- */ // QSA and matchesSelector support // matchesSelector(:active) reports false when true (IE9/Opera 11.5) rbuggyMatches = []; // qSa(:focus) reports false when true (Chrome 21) // We allow this because of a bug in IE8/9 that throws an error // whenever `document.activeElement` is accessed on an iframe // So, we allow :focus to pass through QSA all the time to avoid the IE error // See https://bugs.jquery.com/ticket/13378 rbuggyQSA = []; // querySelectorAll可以说得上兼容性最差得API,每个浏览器每个版本可能都有差异 if ((support.qsa = rnative.test(document.querySelectorAll))) { // Build QSA regex // Regex strategy adopted from Diego Perini assert(function (el) { // Select is set to empty string on purpose // This is to test IE's treatment of not explicitly // setting a boolean content attribute, // since its presence should be enough // https://bugs.jquery.com/ticket/12359 // 对于布尔属性,只要是显式设置了无论值是什么,selected都为false,且通过属性选择器都能选取到 docElem.appendChild(el).innerHTML = "<a id='" + expando + "'></a>" + "<select id='" + expando + "-\r\\' msallowcapture=''>" + "<option selected=''></option></select>"; // Support: IE8, Opera 11-12.16 // Nothing should be selected when empty strings follow ^= or $= or *= // The test attribute must be unknown in Opera but "safe" for WinRT // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section // 如果属性值为空字符串,那么对于^=、&=、*=等操作符直接返回false,不会被匹配 if (el.querySelectorAll("[msallowcapture^='']").length) { rbuggyQSA.push("[*^$]=" + whitespace + "*(?:''|\"\")"); } // Support: IE8 // Boolean attributes and "value" are not treated correctly // 如果为零,则把常见得布尔属性都放到rbuggy列表中。 if (!el.querySelectorAll("[selected]").length) { rbuggyQSA.push("\\[" + whitespace + "*(?:value|" + booleans + ")"); } // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ if (!el.querySelectorAll("[id~=" + expando + "-]").length) { rbuggyQSA.push("~="); } // Webkit/Opera - :checked should return selected option elements // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked // IE8 throws error here and will not see later tests // 根据W3C标准,:checked应该包含被选中得option元素,IE8失败。 if (!el.querySelectorAll(":checked").length) { rbuggyQSA.push(":checked"); } // Support: Safari 8+, iOS 8+ // https://bugs.webkit.org/show_bug.cgi?id=136851 // In-page `selector#id sibling-combinator selector` fails if (!el.querySelectorAll("a#" + expando + "+*").length) { rbuggyQSA.push(".#.+[+~]"); } }); assert(function (el) { el.innerHTML = "<a href='' disabled='disabled'></a>" + "<select disabled='disabled'><option/></select>"; // Support: Windows 8 Native Apps // The type and name attributes are restricted during .innerHTML assignment var input = document.createElement("input"); input.setAttribute("type", "hidden"); el.appendChild(input).setAttribute("name", "D"); // Support: IE8 // Enforce case-sensitivity of name attribute if (el.querySelectorAll("[name=d]").length) { rbuggyQSA.push("name" + whitespace + "*[*^$|!~]?="); } // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) // IE8 throws error here and will not see later tests if (el.querySelectorAll(":enabled").length !== 2) { rbuggyQSA.push(":enabled", ":disabled"); } // Support: IE9-11+ // IE's :disabled selector does not pick up the children of disabled fieldsets docElem.appendChild(el).disabled = true; if (el.querySelectorAll(":disabled").length !== 2) { rbuggyQSA.push(":enabled", ":disabled"); } // Opera 10-11 does not throw on post-comma invalid pseudos // Opera 10-11对于非法伪类不会抛错 el.querySelectorAll("*,:x"); rbuggyQSA.push(",.*:"); }); } // 判定是否支持matchesSelector,如果不支持,看它是否存在带私有前缀得近亲 if ((support.matchesSelector = rnative.test((matches = docElem.matches || docElem.webkitMatchesSelector || docElem.mozMatchesSelector || docElem.oMatchesSelector || docElem.msMatchesSelector)))) { assert(function (el) { // Check to see if it's possible to do matchesSelector // on a disconnected node (IE 9) // IE9缓存过度,能匹配移出DOM树得节点 support.disconnectedMatch = matches.call(el, "*"); // This should fail with an exception // Gecko does not error, returns false instead // Gecko对于非法选择符不会抛错,而是返回false matches.call(el, "[s!='']:x"); rbuggyMatches.push("!=", pseudos); }); } rbuggyQSA = rbuggyQSA.length && new RegExp(rbuggyQSA.join("|")); rbuggyMatches = rbuggyMatches.length && new RegExp(rbuggyMatches.join("|")); /* Contains ---------------------------------------------------------------------- */ hasCompare = rnative.test(docElem.compareDocumentPosition); // Element contains another // Purposefully self-exclusive // Purposefully self-exclusive // As in, an element does not contain itself // 重写contains,有原生API就用原生API,否则就遍历DOM树 // 判断元素是否包含另一个元素,但是排除两个比较的元素是同一个元素的情况,因为元素不能包含它自身。 contains = hasCompare || rnative.test(docElem.contains) ? function (a, b) { var adown = a.nodeType === 9 ? a.documentElement : a, bup = b && b.parentNode; return a === bup || !!(bup && bup.nodeType === 1 && ( adown.contains ? adown.contains(bup) : a.compareDocumentPosition && a.compareDocumentPosition(bup) & 16 )); } : function (a, b) { if (b) { while ((b = b.parentNode)) { if (b === a) { return true; } } } return false; }; /* Sorting排序 ---------------------------------------------------------------------- */ // Document order sorting // 重写比较函数 sortOrder = hasCompare ? function (a, b) { // Flag for duplicate removal // 删除重复标记 if (a === b) { hasDuplicate = true; return 0; } // Sort on method existence if only one input has compareDocumentPosition // 如果只有一个输入具有compareDocumentPosition方法,则对方法存在性排序 /* compareDocumentPosition() 方法比较两个节点,并返回描述它们在文档中位置的整数。请看上面的例子。返回值可能是: 1:没有关系,两个节点不属于同一个文档。 2:第一节点(P1)位于第二个节点后(P2)。 4:第一节点(P1)定位在第二节点(P2)前。 8:第一节点(P1)位于第二节点内(P2)。 16:第二节点(P2)位于第一节点内(P1)。 32:没有关系,或是两个节点是同一元素的两个属性。 注释:返回值可以是值的组合。例如,返回 20 意味着在 p2 在 p1 内部(16),并且 p1 在 p2 之前(4)。 */ var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; if (compare) { return compare; } // Calculate position if both inputs belong to the same document // 如果两个输入属于同一个文档,计算位置 compare = (a.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 || a.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; } : function (a, b) { // Exit early if the nodes are identical // 如果节点相同,则退出 if (a === b) { hasDuplicate = true; return 0; } var cur, i = 0, aup = a.parentNode, bup = b.parentNode, ap = [a], bp = [b]; // Parentless nodes are either documents or disconnected // 如果a或b的父节点是文档或不存在 if (!aup || !bup) { // 判断如果a是文档,直接返回-1 return a === document ? -1 : // 判断b是文档,直接返回1 b === document ? 1 : // 判断a父节点存在,直接返回-1 aup ? -1 : // 判断b父节点存在,直接返回1 bup ? 1 : // 判断sortInput存在 sortInput ? (indexOf(sortInput, a) - indexOf(sortInput, b)) : 0; // If the nodes are siblings, we can do a quick check // 如果节点是兄弟节点,调用siblingCheck()做一个快速检查 } else if (aup === bup) { return siblingCheck(a, b); } // Otherwise we need full lists of their ancestors for comparison // 否则,用while循环将cur的父元素添加到ap数组中。 cur = a; while ((cur = cur.parentNode)) { ap.unshift(cur); } cur = b; while ((cur = cur.parentNode)) { bp.unshift(cur); } // Walk down the tree looking for a discrepancy while (ap[i] === bp[i]) { i++; } return i ? // Do a sibling check if the nodes have a common ancestor // 是否有兄弟节点检查节点是否有共同的祖先 siblingCheck(ap[i], bp[i]) : // Otherwise nodes in our document sort first // 否则,首先对文档中的节点进行排序 ap[i] === preferredDoc ? -1 : bp[i] === preferredDoc ? 1 : 0; }; return document; }; // 筛选出elements满足CSS选择器表达式expr的节点【最终返回的是节点数组】 Sizzle.matches = function (expr, elements) { return Sizzle(expr, null, null, elements); }; // 便捷方法Sizzle.matchesSelector(elem, expr)用于检测某个元素node是否匹配选择器表达式expr。 // 如果浏览器支持原生方法matchesSelector()、mozMatchesSelector()、webkitMatchesSelector()、msMatchesSelector()中的一种,则尝试调用原生方法检查元素与选择器表达式是否匹配;如果浏览器不支持原生方法,或者支持但是检查失败(抛出异常),则调用函数Sizzle(selector, context, results, seed),检查其返回值的长度是否大于0,调用时会将元素elem封装成数组作为参数seed传入。 Sizzle.matchesSelector = function (elem, expr) { // Set document vars if needed // 如果需要,设置文档变量 if ((elem.ownerDocument || elem) !== document) { setDocument(elem); } // Make sure that attribute selectors are quoted // 确保引用了属性选择器 expr = expr.replace(rattributeQuotes, "='$1']"); // 优化使用原生API if (support.matchesSelector && documentIsHTML && !compilerCache[expr + " "] && (!rbuggyMatches || !rbuggyMatches.test(expr)) && (!rbuggyQSA || !rbuggyQSA.test(expr))) { try { var ret = matches.call(elem, expr); // IE 9's matchesSelector returns false on disconnected nodes // ie9的matchesSelector在断开连接的节点上返回false if (ret || support.disconnectedMatch || // As well, disconnected nodes are said to be in a document // 同样,断开连接的节点也在文档中 // fragment in IE 9 elem.document && elem.document.nodeType !== 11) { return ret; } } catch (e) { } } return Sizzle(expr, document, null, [elem]).length > 0; }; // 工具方法Sizzle.contains(context, elem)负责检测元素context是否包含elem。原生方法contains()用于检测一个元素是否包含另一个元素。 Sizzle.contains = function (context, elem) { // Set document vars if needed // 如果需要,设置文档变量 if ((context.ownerDocument || context) !== document) { setDocument(context); } return contains(context, elem); }; // 获取属性 Sizzle.attr = function (elem, name) { // Set document vars if needed // 如果不是当前document if ((elem.ownerDocument || elem) !== document) { setDocument(elem); } // Expr.attrHandle对象包含了:async、autofocus、autoplay、checked、controls、defer、disabled、hidden、ismap、loop、multiple、open、readonly、required、scoped、selectedd等自定义方法(属性) var fn = Expr.attrHandle[name.toLowerCase()], // Don't get fooled by Object.prototype properties (jQuery #13807) // hasOwn是hasOwnProperty,即hasOwn.call()判断传入的属性名是不是这个对象的自定义属性,而不是原型上的属性。 // val为调用自定义方法的结果或underfined val = fn && hasOwn.call(Expr.attrHandle, name.toLowerCase()) ? fn(elem, name, !documentIsHTML) : undefined; // val不等于underfined则返回val return val !== undefined ? val : // support.attributes为真或document不是html,返回elem.getAttribute(name)的值 support.attributes || !documentIsHTML ? elem.getAttribute(name) : // 给val赋值为elem.getAttributeNode(name),且val存在specified,则返回val.value。否则返回null (val = elem.getAttributeNode(name)) && val.specified ? val.value : null; }; Sizzle.escape = function (sel) { return (sel + "").replace(rcssescape, fcssescape); }; // 工具方法Sizzle.error(msg)用于抛出一个含有选择器表达式语法错误信息的异常。 Sizzle.error = function (msg) { throw new Error("Syntax error, unrecognized expression: " + msg); }; /** * Document sorting and removing duplicates * @param {ArrayLike} results */ // 工具方法Sizzle.uniqueSort()负责对元素集合中的元素按照出现在文档中的顺序进行排序,并删除重复元素。 Sizzle.uniqueSort = function (results) { var elem, duplicates = [], j = 0, i = 0; // Unless we *know* we can detect duplicates, assume their presence // 除非我们“知道”我们可以检测到副本,否则就假定它们的存在 hasDuplicate = !support.detectDuplicates; sortInput = !support.sortStable && results.slice(0); results.sort(sortOrder); // 如果变量hasDuplicate为true,表示存在重复元素,则遍历数组results,比较相邻元素是否相等,如果相等则删除。 if (hasDuplicate) { while ((elem = results[i++])) { if (elem === results[i]) { j = duplicates.push(i); } } while (j--) { results.splice(duplicates[j], 1); } } // Clear input after sorting to release objects // See https://github.com/jquery/sizzle/pull/225 // 清除排序后的输入以释放对象 sortInput = null; return results; }; /** * Utility function for retrieving the text value of an array of DOM nodes * @param {Array|Element} elem */ // 工具方法Sizzle.getText(elem)用于获取元素集合中所有元素合并后的文本内容 // 用于:contains伪类,用于匹配当前元素的textConten是否包含目标字符串 // 但对于XML,则需要逐个取得它里面的文本节点,CDTAT节点的nodeValue进行拼接 getText = Sizzle.getText = function (elem) { var node, ret = "", i = 0, nodeType = elem.nodeType; // 如果参数elem不是元素,则默认它是一个数组,递归调用函数getText(node)获取每个元素的文本内容,并合并。 if (!nodeType) { // If no nodeType, this is expected to be an array while ((node = elem[i++])) { // Do not traverse comment nodes // 不遍历注释节点 ret += getText(node); } // 如果elem是元素、DOM树的根节点、邻接节点和它们的子树 } else if (nodeType === 1 || nodeType === 9 || nodeType === 11) { // Use textContent for elements // innerText usage removed for consistency of new lines (jQuery #11153) //对元素使用textContent //为了保持新行一致,删除了innerText用法 if (typeof elem.textContent === "string") { // 返回属性textContent return elem.textContent; } else { // Traverse its children // 遍历它的子元素 for (elem = elem.firstChild; elem; elem = elem.nextSibling) { ret += getText(elem); } } // elem是Text节点 或CDATASection节点 } else if (nodeType === 3 || nodeType === 4) { // 直接返回节点值nodeValue return elem.nodeValue; } // Do not include comment or processing instruction nodes // 不包括注释或处理指令节点 // 最后返回合并后的文本内容 return ret; }; // 对象Sizzle.selectors包含了Sizzle在查找和过滤过程中用到的正则、查找函数、过滤函数 Expr = Sizzle.selectors = { // Can be adjusted by the user // 默认为50,用户可以自行调整 cacheLength: 50, // 创建伪元素 createPseudo: markFunction, // 对象Sizzle.selectors.match中存放了表达式类型和正则得映射,用于确定块表达式得类型,并解析其中得参数。 match: matchExpr, attrHandle: {}, // 对象Sizzle.selectors.find中定义了ID、CLASS、NAME、TAG所对应的查找函数,称为“查找函数集” find: {}, relative: { ">": { dir: "parentNode", first: true }, " ": { dir: "parentNode" }, "+": { dir: "previousSibling", first: true }, "~": { dir: "previousSibling" } }, // Sizzle.selectors.preFilter中定义了类型ATTR\CHILE\PSEUDO,所对应的欲过滤函数,称为“预过滤函数集” preFilter: { // 属性预过滤函数Sizzle。selectors.preFilter.ATTR()负责修正匹配结果match中的属性名和属性值 "ATTR": function (match) { // 修正match[1],即修正属性名 // replace() 方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。 // 删除转义反斜杠,修正某些特殊属性名。 match[1] = match[1].replace(runescape, funescape); // Move the given value to match[3] whether quoted or unquoted // 修正属性值。合并分组3、分组4、分组5的值,删除转义反斜杠。 match[3] = (match[3] || match[4] || match[5] || "").replace(runescape, funescape); // 如果等号部分是~=,表示是单词匹配,则在属性值前后加空格。 // 在过滤函数中,对于~=,会在元素的属性值前后加空格,然后用字符串方法indexOf()检查 if (match[2] === "~=") { match[3] = " " + match[3] + " "; } return match.slice(0, 4); }, // 子元素伪类预过滤函数Sizzle.selectors.preFilter.CHILD(match)负责将伪类的参数格式化。 /* "CHILD": new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i") */ "CHILD": function (match) { // 从matchExpr['CHILD']匹配 /* 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 */ // 将match转换成小写字母的格式 match[1] = match[1].toLowerCase(); // 如果match前三个字母是nth if (match[1].slice(0, 3) === "nth") { // nth-* requires argument // nth-*需要参数,否则阻止参数 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 // Expr.filter.CHILD的数值x和y参数,其中将false/true分别转换为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; }, // 伪类预过滤函数Sizzle.selectors.preFilter.PSEUDO()主要负责处理伪类表达式是:not(selector)的情况,该函数会将匹配结果match中的分组替换为与之匹配的元素集合;对于位置伪类和子元素伪类,则返回true,继续执行各自对应的预过滤函数。 "PSEUDO": function (match) { 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); } }, // 对象Sizzle.selectors.filter中定义了类型PSEUDO、CHILD、TAG、CLASS、ATTR所对应的过滤函数,称为“过滤函数集”。方法调用链为:Sizzle.filter()->Sizzle.selectors.filter[type]。 // 过滤函数负责检测元素是否匹配过滤表达式,返回一个布尔值,其参数格式为: // Sizzle.selectors.filter[类型](元素,正则匹配结果或过滤表达式,下标,元素集合) // 正则匹配结果指Sizzle.selectors.match 中对应的正则匹配块表达式的结果 // 过滤表达式指经过Sizzle.selectors.preFilter处理后的块表达式 filter: { // 标签过滤函数Sizzle.selectors.filter.TAG()用于检查元素的标签名nodeName是否与指定的标签名相等。 "TAG": function (nodeNameSelector) { var nodeName = nodeNameSelector.replace(runescape, funescape).toLowerCase(); return nodeNameSelector === "*" ? function () { return true; } : function (elem) { return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; }; }, // 类样式过滤函数Sizzle.slectors.filter.CLASS()用于检查元素的类样式className是否含有指定的类样式。检查技巧是在类样式前后加空格,然后判断字符串方法indexOf()的返回值。 "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") || ""); }); }, // 属性过滤函数Sizzle.selectors.filter.ATTR()用于检查元素的属性是否匹配属性表达式。 "ATTR": function (name, operator, check) { return function (elem) { // 变量result是元素的HTML属性值或DOM属性值。 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; }; }, // 子元素伪类过滤函数Sizzle.selectors.filter.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, uniqueCache, outerCache, node, nodeIndex, start, dir = simple !== forward ? "nextSibling" : "previousSibling", parent = elem.parentNode, name = ofType && elem.nodeName.toLowerCase(), useCache = !xml && !ofType, diff = false; 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 // ...in a gzip-friendly way node = parent; outerCache = node[expando] || (node[expando] = {}); // Support: IE <9 only // Defend against cloned attroperties (jQuery gh-1709) uniqueCache = outerCache[node.uniqueID] || (outerCache[node.uniqueID] = {}); cache = uniqueCache[type] || []; nodeIndex = cache[0] === dirruns && cache[1]; diff = nodeIndex && 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) { uniqueCache[type] = [dirruns, nodeIndex, diff]; break; } } } else { // Use previously-cached element index if available if (useCache) { // ...in a gzip-friendly way node = elem; outerCache = node[expando] || (node[expando] = {}); // Support: IE <9 only // Defend against cloned attroperties (jQuery gh-1709) uniqueCache = outerCache[node.uniqueID] || (outerCache[node.uniqueID] = {}); cache = uniqueCache[type] || []; nodeIndex = cache[0] === dirruns && cache[1]; diff = nodeIndex; } // xml :nth-child(...) // or :nth-last-child(...) or :nth(-last)?-of-type(...) if (diff === false) { // 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) { outerCache = node[expando] || (node[expando] = {}); // Support: IE <9 only // Defend against cloned attroperties (jQuery gh-1709) uniqueCache = outerCache[node.uniqueID] || (outerCache[node.uniqueID] = {}); uniqueCache[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); } }; }, // 伪类过滤函数Sizzle.selectors.filter.PSEUDO()用于检查元素是否匹配伪类。大部分检查通过调用伪类过滤函数集Sizzle.selectors.filters中对应的伪类过滤函数来实现 "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 // 伪类名不区分大小写 // 按大小写区分优先级,以防用大写字母添加自定义伪代码 // 记住setFilters继承自伪代码 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 // 用户可以使用createPseudo来表示 // 创建过滤器函数需要参数 // 就像Sizzle一样 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; } }, pseudos: { // Potentially complex pseudos // 潜在的复杂伪类 // $(':not(select)') 去除所有与给定选择器匹配的元素 "not": markFunction(function (selector) { // Trim the selector passed to compile // to avoid treating leading and trailing // spaces as combinators var input = [], results = [], matcher = compile(selector.replace(rtrim, "$1")); return matcher[expando] ? markFunction(function (seed, matches, context, xml) { var elem, unmatched = matcher(seed, null, xml, []), i = seed.length; // Match elements unmatched by `matcher` while (i--) { if ((elem = unmatched[i])) { seed[i] = !(matches[i] = elem); } } }) : function (elem, context, xml) { input[0] = elem; matcher(input, null, xml, results); // Don't keep the element (issue #299) input[0] = null; return !results.pop(); }; }), // $(':has(selector)') 匹配含有选择器所匹配元素的元素 "has": markFunction(function (selector) { return function (elem) { return Sizzle(selector, elem).length > 0; }; }), // $(':contains(selector)') 匹配包含给定文本的元素 "contains": markFunction(function (text) { text = text.replace(runescape, funescape); return function (elem) { return (elem.textContent || elem.innerText || getText(elem)).indexOf(text) > -1; }; }), // "Whether an element is represented by a :lang() selector // is based solely on the element's language value // being equal to the identifier C, // or beginning with the identifier C immediately followed by "-". // The matching of C against the element's language value is performed case-insensitively. // The identifier C does not have to be a valid language name." // http://www.w3.org/TR/selectors/#lang-pseudo /* $(':lang(it)') 选择指定语言的所有元素。 :lang选择器,匹配有一个语言值等于所提供的语言代码,或以提供的语言代码开始,后面马上跟一个“ - ”的元素。例如,选择器$("div:lang(en)")将匹配<div lang="en"> and <div lang="en-us">(和他们的后代<div>),但不包括<div lang="fr"> 对于HTML元素,语言值由lang属性决定,也可能由来自meta元素或HTTP头信息决定。 */ "lang": markFunction(function (lang) { // lang value must be a valid identifier if (!ridentifier.test(lang || "")) { Sizzle.error("unsupported lang: " + lang); } lang = lang.replace(runescape, funescape).toLowerCase(); return function (elem) { var elemLang; do { if ((elemLang = documentIsHTML ? elem.lang : elem.getAttribute("xml:lang") || elem.getAttribute("lang"))) { elemLang = elemLang.toLowerCase(); return elemLang === lang || elemLang.indexOf(lang + "-") === 0; } } while ((elem = elem.parentNode) && elem.nodeType === 1); return false; }; }), // Miscellaneous /* 选择由文档URI的格式化识别码表示的目标元素。 如果文档的URI包含一个格式化的标识符,或hash(哈希), 然后:target选择器将匹配ID和标识符相匹配的元素。 例如,给定的URI http://example.com/#foo, $( "p:target" ),将选择<p id="foo">元素。 */ "target": function (elem) { var hash = window.location && window.location.hash; return hash && hash.slice(1) === elem.id; }, /* 选择该文档的根元素。 在HTML中,文档的根元素,和$(":root")选择的元素一样, 永远是<html>元素。 */ "root": function (elem) { return elem === docElem; }, // $(':focus') 匹配当前焦点元素 "focus": function (elem) { return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); }, // Boolean properties // 布尔属性 // $(':enabled') 匹配所有可用元素(未禁用的,不隐藏的) "enabled": createDisabledPseudo(false), // $(':disabled') 匹配所有不可用元素(禁用的) "disabled": createDisabledPseudo(true), // $(':checked') 匹配所有选中的被选中元素,包括复选框、单选按钮、不包括option元素 "checked": function (elem) { // In CSS3, :checked should return both checked and selected elements // 在CSS3中,:checked应该同时返回已选中和选中的元素 // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked var nodeName = elem.nodeName.toLowerCase(); return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); }, // $(':selected') 匹配所有选中的option元素 "selected": function (elem) { // Accessing this property makes selected-by-default // options in Safari work properly //默认情况下,访问此属性将选择 // Safari中的选项可以正常工作 if (elem.parentNode) { elem.parentNode.selectedIndex; } return elem.selected === true; }, // Contents // 内容 // $(':empty') 匹配所有不包含子元素或者文本的空元素 "empty": function (elem) { // http://www.w3.org/TR/selectors/#empty-pseudo // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), // but not by others (comment: 8; processing instruction: 7; etc.) // nodeType < 6 works because attributes (2) do not appear as children for (elem = elem.firstChild; elem; elem = elem.nextSibling) { if (elem.nodeType < 6) { return false; } } return true; }, // $(':parent') 匹配所有含有子元素或文本的元素 "parent": function (elem) { return !Expr.pseudos["empty"](elem); }, // Element/input types元素/输入类型 // $(':header') 匹配如h1、h2、h3之类的标题元素 "header": function (elem) { return rheader.test(elem.nodeName); }, // $(':input') 匹配所有input\textarea\select\button元素 "input": function (elem) { return rinputs.test(elem.nodeName); }, // $(':button') 匹配所有按钮 "button": function (elem) { var name = elem.nodeName.toLowerCase(); return name === "input" && elem.type === "button" || name === "button"; }, // $(':text') 匹配所有单行文本框 "text": function (elem) { var attr; return elem.nodeName.toLowerCase() === "input" && elem.type === "text" && // Support: IE<8 // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" ((attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text"); }, // Position-in-collection // 在集合中的位置 // $(':first') 匹配找到的第一个元素 "first": createPositionalPseudo(function () { return [0]; }), // $(':last') 匹配找到的最后一个元素 "last": createPositionalPseudo(function (matchIndexes, length) { return [length - 1]; }), // $(':eq(index)') 匹配一个指定下标的元素,从0开始计数 "eq": createPositionalPseudo(function (matchIndexes, length, argument) { return [argument < 0 ? argument + length : argument]; }), // $(':even') 匹配所有下标为偶数的元素,从0开始计数 "even": createPositionalPseudo(function (matchIndexes, length) { var i = 0; for (; i < length; i += 2) { matchIndexes.push(i); } return matchIndexes; }), // $(':odd') 匹配所有下标为奇数的元素,从0开始计数 "odd": createPositionalPseudo(function (matchIndexes, length) { var i = 1; for (; i < length; i += 2) { matchIndexes.push(i); } return matchIndexes; }), // $(':lt(index)') 匹配所有小于指定下标的元素 "lt": createPositionalPseudo(function (matchIndexes, length, argument) { var i = argument < 0 ? argument + length : argument; for (; --i >= 0;) { matchIndexes.push(i); } return matchIndexes; }), // $('gt:(index)') 匹配所有大于指定下标的元素 "gt": createPositionalPseudo(function (matchIndexes, length, argument) { var i = argument < 0 ? argument + length : argument; for (; ++i < length;) { matchIndexes.push(i); } return matchIndexes; }) } }; // $(':nth(index)') 匹配一个指定下标的元素,从0开始计数 Expr.pseudos["nth"] = Expr.pseudos["eq"]; // Add button/input type pseudos // 给Expr.pseudos添加按钮/输入类型伪代码 for (i in { radio: true, checkbox: true, file: true, password: true, image: true }) { Expr.pseudos[i] = createInputPseudo(i); } for (i in { submit: true, reset: true }) { Expr.pseudos[i] = createButtonPseudo(i); } // Easy API for creating new setFilters // 用于创建新的setFilters的简单API function setFilters() { } setFilters.prototype = Expr.filters = Expr.pseudos; // 对象Sizzle.selectors.setFilters中定义了一组位置伪类和对应的伪类过滤函数,称为“位置伪类过滤函数集”。方法调用链为:Sizzle.filter()->Sizzle.selectors.filter.POS()->Sizzle.selectors.setFilters Expr.setFilters = new setFilters(); // 标记 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 = []; // [ [{value:"title",type:"TAG",matches:["title"]}], // [{value:"div",type:["TAG",matches:["div"]}, // {value:">", type: ">"}, // {value:":nth-child(even)",type:"CHILD",matches:["nth", // "child","even",2,0,undefined,undefined,undefined]} // ] // ] // 有多少个并联选择器,里面就有多少个数组,数组里面时用于value于type的对象 preFilters = Expr.preFilter; while (soFar) { // Comma and first run // 以第一个逗号切割选择符,然后去掉前面的部分 if (!matched || (match = rcomma.exec(soFar))) { if (match) { // Don't consume trailing commas as valid //不要使用尾随逗号 soFar = soFar.slice(match[0].length) || soFar; } groups.push((tokens = [])); } matched = false; // Combinators // 将刚才前面的部分以关系选择器在进行划分 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); } // Filters // 将每个选择器组依次用ID,TAG,CLASS,ATTR,CHILD,PSEUDO这些正则进行匹配 for (type in Expr.filter) { // preFilters是用于分析选择器的名字与参数 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; } } // Return the length of the invalid excess // if we're just parsing // Otherwise, throw an error or return tokens //返回无效余数的长度 //如果我们只是解析 //否则,抛出错误或返回令牌 return parseOnly ? soFar.length : soFar ? Sizzle.error(selector) : // Cache the tokens // 缓存的令牌,放到tokenCache函数里进行缓存 tokenCache(selector, groups).slice(0); }; // toSelector()用于连接多个选择器,并以字符串的形式返回 function toSelector(tokens) { // 将符合流重新组合成选择符,这时能把多余的空白去掉 var i = 0, len = tokens.length, selector = ""; for (; i < len; i++) { selector += tokens[i].value; } return selector; } // addCombinator()用于添加选择符 function addCombinator(matcher, combinator, base) { var dir = combinator.dir, skip = combinator.next, key = skip || dir, checkNonElements = base && key === "parentNode", doneName = done++; return combinator.first ? // Check against closest ancestor/preceding element // 检查最近的祖先/前面的元素 function (elem, context, xml) { while ((elem = elem[dir])) { if (elem.nodeType === 1 || checkNonElements) { return matcher(elem, context, xml); } } return false; } : // Check against all ancestor/preceding elements // 检查所有祖先/前面的元素 function (elem, context, xml) { var oldCache, uniqueCache, outerCache, newCache = [dirruns, doneName]; // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching if (xml) { while ((elem = elem[dir])) { if (elem.nodeType === 1 || checkNonElements) { if (matcher(elem, context, xml)) { return true; } } } } else { while ((elem = elem[dir])) { if (elem.nodeType === 1 || checkNonElements) { outerCache = elem[expando] || (elem[expando] = {}); // Support: IE <9 only // Defend against cloned attroperties (jQuery gh-1709) uniqueCache = outerCache[elem.uniqueID] || (outerCache[elem.uniqueID] = {}); if (skip && skip === elem.nodeName.toLowerCase()) { elem = elem[dir] || elem; } else if ((oldCache = uniqueCache[key]) && oldCache[0] === dirruns && oldCache[1] === doneName) { // Assign to newCache so results back-propagate to previous elements return (newCache[2] = oldCache[2]); } else { // Reuse newcache so results back-propagate to previous elements uniqueCache[key] = newCache; // A match means we're done; a fail means we have to keep checking if ((newCache[2] = matcher(elem, context, xml))) { return true; } } } } } return false; }; } // 元素匹配器 function elementMatcher(matchers) { return matchers.length > 1 ? function (elem, context, xml) { var i = matchers.length; while (i--) { if (!matchers[i](elem, context, xml)) { return false; } } return true; } : matchers[0]; } // 多个上下文 function multipleContexts(selector, contexts, results) { var i = 0, len = contexts.length; for (; i < len; i++) { Sizzle(selector, contexts[i], results); } return results; } // 压缩 function condense(unmatched, map, filter, context, xml) { var elem, newUnmatched = [], i = 0, len = unmatched.length, mapped = map != null; for (; i < len; i++) { if ((elem = unmatched[i])) { if (!filter || filter(elem, context, xml)) { newUnmatched.push(elem); if (mapped) { map.push(i); } } } } return newUnmatched; } // 设置选择器 function setMatcher(preFilter, selector, matcher, postFilter, postFinder, postSelector) { if (postFilter && !postFilter[expando]) { postFilter = setMatcher(postFilter); } if (postFinder && !postFinder[expando]) { postFinder = setMatcher(postFinder, postSelector); } return markFunction(function (seed, results, context, xml) { var temp, i, elem, preMap = [], postMap = [], preexisting = results.length, // Get initial elements from seed or context elems = seed || multipleContexts(selector || "*", context.nodeType ? [context] : context, []), // Prefilter to get matcher input, preserving a map for seed-results synchronization matcherIn = preFilter && (seed || !selector) ? condense(elems, preMap, preFilter, context, xml) : elems, matcherOut = matcher ? // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, postFinder || (seed ? preFilter : preexisting || postFilter) ? // ...intermediate processing is necessary [] : // ...otherwise use results directly results : matcherIn; // Find primary matches if (matcher) { matcher(matcherIn, matcherOut, context, xml); } // Apply postFilter if (postFilter) { temp = condense(matcherOut, postMap); postFilter(temp, [], context, xml); // Un-match failing elements by moving them back to matcherIn i = temp.length; while (i--) { if ((elem = temp[i])) { matcherOut[postMap[i]] = !(matcherIn[postMap[i]] = elem); } } } if (seed) { if (postFinder || preFilter) { if (postFinder) { // Get the final matcherOut by condensing this intermediate into postFinder contexts temp = []; i = matcherOut.length; while (i--) { if ((elem = matcherOut[i])) { // Restore matcherIn since elem is not yet a final match temp.push((matcherIn[i] = elem)); } } postFinder(null, (matcherOut = []), temp, xml); } // Move matched elements from seed to results to keep them synchronized i = matcherOut.length; while (i--) { if ((elem = matcherOut[i]) && (temp = postFinder ? indexOf(seed, elem) : preMap[i]) > -1) { seed[temp] = !(results[temp] = elem); } } } // Add elements to results, through postFinder if defined } else { matcherOut = condense( matcherOut === results ? matcherOut.splice(preexisting, matcherOut.length) : matcherOut ); if (postFinder) { postFinder(null, results, matcherOut, xml); } else { push.apply(results, matcherOut); } } }); } // 生成用于匹配当个选择器组的函数 function matcherFromTokens(tokens) { var checkContext, matcher, j, len = tokens.length, leadingRelative = Expr.relative[tokens[0].type], implicitRelative = leadingRelative || Expr.relative[" "], i = leadingRelative ? 1 : 0, // The foundational matcher ensures that elements are reachable from top-level context(s) // 基本匹配程序确保可以从顶级上下文访问元素 matchContext = addCombinator(function (elem) { return elem === checkContext; }, implicitRelative, true), matchAnyContext = addCombinator(function (elem) { return indexOf(checkContext, elem) > -1; }, implicitRelative, true), matchers = [function (elem, context, xml) { var ret = (!leadingRelative && (xml || context !== outermostContext)) || ( (checkContext = context).nodeType ? matchContext(elem, context, xml) : matchAnyContext(elem, context, xml)); // Avoid hanging onto element (issue #299) checkContext = null; return ret; }]; for (; i < len; i++) { if ((matcher = Expr.relative[tokens[i].type])) { matchers = [addCombinator(elementMatcher(matchers), matcher)]; } else { matcher = Expr.filter[tokens[i].type].apply(null, tokens[i].matches); // Return special upon seeing a positional matcher // 看到位置匹配器后返回特殊值 if (matcher[expando]) { // Find the next relative operator (if any) for proper handling // 找到下一个相关操作符(如果有的话)以便正确处理 j = ++i; for (; j < len; j++) { if (Expr.relative[tokens[j].type]) { break; } } return setMatcher( i > 1 && elementMatcher(matchers), i > 1 && toSelector( // If the preceding token was a descendant combinator, insert an implicit any-element `*` // 如果前面的令牌是后代组合符,则插入一个隐式的任意元素“*” tokens.slice(0, i - 1).concat({ value: tokens[i - 2].type === " " ? "*" : "" }) ).replace(rtrim, "$1"), matcher, i < j && matcherFromTokens(tokens.slice(i, j)), j < len && matcherFromTokens((tokens = tokens.slice(j))), j < len && toSelector(tokens) ); } matchers.push(matcher); } } return elementMatcher(matchers); } // 生成用于匹配单个选择器群组的函数 function matcherFromGroupMatchers(elementMatchers, setMatchers) { var bySet = setMatchers.length > 0, byElement = elementMatchers.length > 0, superMatcher = function (seed, context, xml, results, outermost) { var elem, j, matcher, matchedCount = 0, i = "0", unmatched = seed && [], setMatched = [], contextBackup = outermostContext, // We must always have either seed elements or outermost context elems = seed || byElement && Expr.find["TAG"]("*", outermost), // Use integer dirruns iff this is the outermost matcher dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), len = elems.length; if (outermost) { outermostContext = context === document || context || outermost; } // Add elements passing elementMatchers directly to results // Support: IE<9, Safari // Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id for (; i !== len && (elem = elems[i]) != null; i++) { if (byElement && elem) { j = 0; if (!context && elem.ownerDocument !== document) { setDocument(elem); xml = !documentIsHTML; } while ((matcher = elementMatchers[j++])) { if (matcher(elem, context || document, xml)) { results.push(elem); break; } } if (outermost) { dirruns = dirrunsUnique; } } // Track unmatched elements for set filters if (bySet) { // They will have gone through all possible matchers if ((elem = !matcher && elem)) { matchedCount--; } // Lengthen the array for every element, matched or not if (seed) { unmatched.push(elem); } } } // `i` is now the count of elements visited above, and adding it to `matchedCount` // makes the latter nonnegative. matchedCount += i; // Apply set filters to unmatched elements // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` // equals `i`), unless we didn't visit _any_ elements in the above loop because we have // no element matchers and no seed. // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that // case, which will result in a "00" `matchedCount` that differs from `i` but is also // numerically zero. if (bySet && i !== matchedCount) { j = 0; while ((matcher = setMatchers[j++])) { matcher(unmatched, setMatched, context, xml); } if (seed) { // Reintegrate element matches to eliminate the need for sorting if (matchedCount > 0) { while (i--) { if (!(unmatched[i] || setMatched[i])) { setMatched[i] = pop.call(results); } } } // Discard index placeholder values to get only actual matches setMatched = condense(setMatched); } // Add matches to results push.apply(results, setMatched); // Seedless set matches succeeding multiple successful matchers stipulate sorting if (outermost && !seed && setMatched.length > 0 && (matchedCount + setMatchers.length) > 1) { Sizzle.uniqueSort(results); } } // Override manipulation of globals by nested matchers if (outermost) { dirruns = dirrunsUnique; outermostContext = contextBackup; } return unmatched; }; return bySet ? markFunction(superMatcher) : superMatcher; } // 进行编译 compile = Sizzle.compile = function (selector, match /* Internal Use Only 内部转用*/) { var i, setMatchers = [], elementMatchers = [], cached = compilerCache[selector + " "]; if (!cached) { // Generate a function of recursive functions that can be used to check each element // 生成一个递归函数的函数,可用于检查每个元素 if (!match) { match = tokenize(selector); } i = match.length; while (i--) { cached = matcherFromTokens(match[i]); if (cached[expando]) { setMatchers.push(cached); } else { elementMatchers.push(cached); } } // Cache the compiled function // 缓存编译后的函数 cached = compilerCache(selector, matcherFromGroupMatchers(elementMatchers, setMatchers)); // Save selector and tokenization // 保存选择器和标记 cached.selector = selector; } return cached; }; /** * A low-level selection function that works with Sizzle's compiled * selector functions * @param {String|Function} selector A selector or a pre-compiled * selector function built with Sizzle.compile * @param {Element} context * @param {Array} [results] * @param {Array} [seed] A set of elements to match against */ /* 编译了一个使用Sizzle的低级选择函数 选择器函数 */ select = Sizzle.select = function (selector, context, results, seed) { // 参数selector:格式为string或function,使用sizz .compile构建选择器函数 // 参数context:格式为element // 参数results:格式为array // 参数seed:格式为array,存放要匹配元素的数组 var i, tokens, token, type, find, compiled = typeof selector === "function" && selector, match = !seed && tokenize((selector = compiled.selector || selector)); results = results || []; // Try to minimize operations if there is only one selector in the list and no seed // (the latter of which guarantees us context) // 如果在列表种只有一个选择器而没有seed,则尽量减少操作,来保证上下文的环境 // 如果只有一个选择器群组 if (match.length === 1) { // Reduce context if the leading compound selector is an ID // 如果前面的复合选择器是ID,则减少上下文 tokens = match[0] = match[0].slice(0); // 如果里面包含ID选择器,比如#body>div if (tokens.length > 2 && (token = tokens[0]).type === "ID" && context.nodeType === 9 && documentIsHTML && Expr.relative[tokens[1].type]) { context = (Expr.find["ID"](token.matches[0].replace(runescape, funescape), context) || [])[0]; // 如果最左边的那个祖先都不存在,那么就不用找下去了 if (!context) { return results; // Precompiled matchers will still verify ancestry, so step up a level // 预编译的匹配器仍将验证祖先,因此提高一级 } else if (compiled) { context = context.parentNode; } // 将最初的选择符去掉ID选择器---> ">div" selector = selector.slice(tokens.shift().value.length); } // Fetch a seed set for right-to-left matching // 取一个种子集进行从右到左的匹配 i = matchExpr["needsContext"].test(selector) ? 0 : tokens.length; while (i--) { token = tokens[i]; // Abort if we hit a combinator if (Expr.relative[(type = token.type)]) { break; } // find查找器有ID,TAG,NAME,CALSS,都是对应原生API,例如:div:not(.a),div优先被处理 if ((find = Expr.find[type])) { // Search, expanding context for leading sibling combinators // 扩展主要兄弟组合符的上下文 if ((seed = find( token.matches[0].replace(runescape, funescape), rsibling.test(tokens[0].type) && testContext(context.parentNode) || context ))) { // If seed is empty or no tokens remain, we can return early // 如果seed是空的或者没有留下任何标记,我们可以提前返回 // 然后去掉用过的选择器,比如上例中的div tokens.splice(i, 1); selector = seed.length && toSelector(tokens); // 如果选择符为空白,那么将种子集合并到结构集中 if (!selector) { push.apply(results, seed); return results; } break; } } } } // Compile and execute a filtering function if one is not provided // Provide `match` to avoid retokenization if we modified the selector above //如果没有提供过滤函数,编译并执行它 //如果我们修改了上面的选择器,请提供“match”以避免重新标记 (compiled || compile(selector, match))( seed, context, !documentIsHTML, results, !context || rsibling.test(selector) && testContext(context.parentNode) || context ); return results; }; // One-time assignments // Sort stability // 排序穩定 support.sortStable = expando.split("").sort(sortOrder).join("") === expando; // Support: Chrome 14-35+ // Always assume duplicates if they aren't passed to the comparison function // 如果没有将副本传递给比较函数,则始终假定它们是重复的 support.detectDuplicates = !!hasDuplicate; // Initialize against the default document // 初始化默认文档 setDocument(); // Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) // Detached nodes confoundingly follow *each other* // 分离的节点混乱的跟随*彼此* support.sortDetached = assert(function (el) { // Should return 1, but returns 4 (following) return el.compareDocumentPosition(document.createElement("fieldset")) & 1; }); // Support: IE<8 // Prevent attribute/property "interpolation" // 防止属性/属性“插值” // https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx if (!assert(function (el) { el.innerHTML = "<a href='#'></a>"; return el.firstChild.getAttribute("href") === "#"; })) { addHandle("type|href|height|width", function (elem, name, isXML) { if (!isXML) { return elem.getAttribute(name, name.toLowerCase() === "type" ? 1 : 2); } }); } // Support: IE<9 // Use defaultValue in place of getAttribute("value") // 使用defaultValue代替getAttribute(“value”) if (!support.attributes || !assert(function (el) { el.innerHTML = "<input/>"; el.firstChild.setAttribute("value", ""); return el.firstChild.getAttribute("value") === ""; })) { addHandle("value", function (elem, name, isXML) { if (!isXML && elem.nodeName.toLowerCase() === "input") { return elem.defaultValue; } }); } // Support: IE<9 // Use getAttributeNode to fetch booleans when getAttribute lies // 当getAttribute保留时,使用getAttributeNode获取布尔值 if ( !assert(function (el) { return el.getAttribute("disabled") == null;}) ) { addHandle(booleans, function (elem, name, isXML) { var val; if (!isXML) { return elem[name] === true ? name.toLowerCase() : (val = elem.getAttributeNode(name)) && val.specified ? val.value : null; } }); } return Sizzle; })(window); // jQuery扩展 // 下面的代码将Sizzle的方法和属性暴露给了jQuery jQuery.find = Sizzle; jQuery.expr = Sizzle.selectors; // Deprecated // 将要被弃用的jquery扩展 jQuery.expr[":"] = jQuery.expr.pseudos; jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; jQuery.text = Sizzle.getText; jQuery.isXMLDoc = Sizzle.isXML; jQuery.contains = Sizzle.contains; jQuery.escapeSelector = Sizzle.escape; // jQuery扩展的外部引用 // 代码行:2770——3189 var dir = function( elem, dir, until ) { var matched = [], truncate = until !== undefined; while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { if ( elem.nodeType === 1 ) { if ( truncate && jQuery( elem ).is( until ) ) { break; } matched.push( elem ); } } return matched; }; var siblings = function( n, elem ) { var matched = []; for ( ; n; n = n.nextSibling ) { if ( n.nodeType === 1 && n !== elem ) { matched.push( n ); } } return matched; }; var rneedsContext = jQuery.expr.match.needsContext; function nodeName( elem, name ) { return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); }; var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); // Implement the identical functionality for filter and not // 为filter和not实现相同的功能 function winnow(elements, qualifier, not) { // 函数winnow(elements,qualifier,not)负责过滤元素集合,它接受三个参数: // 参数elements:待过滤的元素集合 // 参数qualifier:用于过滤元素集合elements,可选值有函数、DOM元素、选择器表达式、DOM元素数组、jQuery对象。 // 参数not:布尔值。如果为true,则保留匹配元素,如果为false,则保留不匹配元素。 // 如果参数qualifier是函数,则调用方法jQuery.grep(elements, function(elem, i))遍历元素集合elements,在每个元素上执行该函数,然后将返回值与参数keep进行比较,如果一致则保留,不一致则丢弃。 if (isFunction(qualifier)) { return jQuery.grep(elements, function (elem, i) { return !!qualifier.call(elem, i, elem) !== not; }); } // Single element // 如果参数qualifier是DOM元素,则调用方法jQuery.grep(elements,function(elem))遍历元素集合elements,检查其中的每个元素是否与参数qualifier相等,然后将检查结果与参数not进行比较,如果一致则保留,不一致则丢弃。 if (qualifier.nodeType) { return jQuery.grep(elements, function (elem) { return (elem === qualifier) !== not; }); } // Arraylike of elements (jQuery, arguments, Array) // 如果参数qualifier是字符串,则调用方法jQuery.grep(elements,function(elem))遍历元素集合elements,检查其中的每个元素是否与参数qualifier相等,然后将检查结果与参数not进行比较,如果一致则保留,不一致则丢弃。 if (typeof qualifier !== "string") { return jQuery.grep(elements, function (elem) { return (indexOf.call(qualifier, elem) > -1) !== not; }); } // Filtered directly for both simple and complex selectors // 直接为简单和复杂的选择器过滤 return jQuery.filter(qualifier, elements, not); } jQuery.filter = function( expr, elems, not ) { var elem = elems[ 0 ]; if ( not ) { expr = ":not(" + expr + ")"; } if ( elems.length === 1 && elem.nodeType === 1 ) { return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; } return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { return elem.nodeType === 1; } ) ); }; jQuery.fn.extend({ // 方法.find(selector)用于获取匹配元素集合中的每个元素的后代元素,可以用一个选择器表达式、jQuery对象或DOM元素来过滤查找结果,并用匹配的元素构造一个新的jQuery对象作为返回值。改方法通过调用函数Sizzle(selector, context, results,seed)来实现,并在后者的基础上增加了对多上下文和链式语法的支持 find: function (selector) { var i, ret, len = this.length, self = this; // 如果参数selector不是字符串,则认为该参数时jQuery对象或DOM元素,此时先将该参数统一封装为一个新jQuery对象,然后遍历新jQuery对象,检查其中的元素是否时当前jQuery对象中某个元素的后代元素,如果是则保留,不是则丢弃.最后返回含有新jQuery对象子集的另一个新jQuery对象. if (typeof selector !== "string") { return this.pushStack(jQuery(selector).filter(function () { for (i = 0; i < len; i++) { if (jQuery.contains(self[i], this)) { return true; } } })); } // 调用方法.pushStack()构造一个新的空jQuery对象,并将其作为返回值,后面找到的元素都将被添加到该jQuery对象中. ret = this.pushStack([]); // 如果参数selector是字符串,则遍历当前元素集合,以每个元素为上下文,调用方法jQuery.find(selector, context, results, seed)也就是Sizzle(selector, context, results, seed),查找匹配的后代元素,并将查找结果合并、去重. for (i = 0; i < len; i++) { jQuery.find(selector, self[i], ret); } // 如果长度大于1,则调用uniqueSort(ret)排序数组,并移除任何重复节点,以确保找到的元素是唯一的.否则直接返回ret return len > 1 ? jQuery.uniqueSort(ret) : ret; }, // 方法.filter(selector)和方法.not(selector)通过调用函数winnow(elements, qualifier, not)对当前匹配元素集合进行过滤,并用其返回值构造一个新jQuery对象。不过,这两个方法的过滤行为正好相反,这种差异通过调用函数winnow(elements, qualifier, not)时传入不同的参数not来实现。 // 方法.filter(selector)用当前jQuery对象的子集构造一个新jQuery对象,其中只保留与参数selector匹配的元素。参数selector可以是选择器表达式、jQuery对象、函数、DOM元素或DOM元素数组。 filter: function (selector) { return this.pushStack(winnow(this, selector || [], false)); }, // 方法.not(selector)用当前jQuery对象的子集构造一个新jQuery对象,其中只保留与参数selector不匹配的元素。参数selector可以是选择器表达式、jQuery对象、函数、DOM元素或DOM元素数组。 not: function (selector) { return this.pushStack(winnow(this, selector || [], true)); }, // 方法.is(selector)用选择器表达式、DOM元素、jQuery对象或函数来检查当前匹配元素集合,只要其中某个元素可以匹配给定的参数就返回true。 is: function (selector) { return !!winnow( this, // If this is a positional/relative selector, check membership in the returned set // so $("p:first").is("p:last") won't return true for a doc with two "p". //如果这是一个位置/相对选择器,检查返回集合中的成员关系 //因此,$("p:first").is("p:last")对于带有两个"p"的文档不会返回true。 // 如果参数selector是字符串且含有位置伪类,则调用构造函数jQuery(selector)查找参数selector匹配的元素集合,然后检查当前匹配元素集合中的第一个元素是否在查找结果中,如果在结果中则返回true,如果不在则返回false。否则返回selector或者空数组。 typeof selector === "string" && rneedsContext.test(selector) ? jQuery(selector) : selector || [], false ).length; } }); // 初始化jQuery对象 // Initialize a jQuery object // A central reference to the root jQuery(document) var rootjQuery, // A simple way to check for HTML strings // Prioritize #id over <tag> to avoid XSS via location.hash (#9521) // Strict HTML recognition (#11290: must start with <) // Shortcut simple #id case for speed rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, init = jQuery.fn.init = function( selector, context, root ) { var match, elem; // HANDLE: $(""), $(null), $(undefined), $(false) if ( !selector ) { return this; } // Method init() accepts an alternate rootjQuery // so migrate can support jQuery.sub (gh-2101) root = root || rootjQuery; // Handle HTML strings if ( typeof selector === "string" ) { if ( selector[ 0 ] === "<" && selector[ selector.length - 1 ] === ">" && selector.length >= 3 ) { // Assume that strings that start and end with <> are HTML and skip the regex check match = [ null, selector, null ]; } else { match = rquickExpr.exec( selector ); } // Match html or make sure no context is specified for #id if ( match && ( match[ 1 ] || !context ) ) { // HANDLE: $(html) -> $(array) if ( match[ 1 ] ) { context = context instanceof jQuery ? context[ 0 ] : context; // Option to run scripts is true for back-compat // Intentionally let the error be thrown if parseHTML is not present jQuery.merge( this, jQuery.parseHTML( match[ 1 ], context && context.nodeType ? context.ownerDocument || context : document, true ) ); // HANDLE: $(html, props) if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { for ( match in context ) { // Properties of context are called as methods if possible if ( isFunction( this[ match ] ) ) { this[ match ]( context[ match ] ); // ...and otherwise set as attributes } else { this.attr( match, context[ match ] ); } } } return this; // HANDLE: $(#id) } else { elem = document.getElementById( match[ 2 ] ); if ( elem ) { // Inject the element directly into the jQuery object this[ 0 ] = elem; this.length = 1; } return this; } // HANDLE: $(expr, $(...)) } else if ( !context || context.jquery ) { return ( context || root ).find( selector ); // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { return this.constructor( context ).find( selector ); } // HANDLE: $(DOMElement) } else if ( selector.nodeType ) { this[ 0 ] = selector; this.length = 1; return this; // HANDLE: $(function) // Shortcut for document ready } else if ( isFunction( selector ) ) { return root.ready !== undefined ? root.ready( selector ) : // Execute immediately if ready is not present selector( jQuery ); } return jQuery.makeArray( selector, this ); }; // Give the init function the jQuery prototype for later instantiation init.prototype = jQuery.fn; // Initialize central reference rootjQuery = jQuery( document ); var rparentsprev = /^(?:parents|prev(?:Until|All))/, // Methods guaranteed to produce a unique set when starting from a unique set guaranteedUnique = { children: true, contents: true, next: true, prev: true }; jQuery.fn.extend({ // 方法.has(target)用当前jQuery对象的子集构造一个新jQuery对象,其中只保留子元素可以匹配参数target的元素。参数target可以是选择器表达式、jQuery对象或DOM元素。该方法通过调用方法.filter(selector)遍历当前匹配元素集合,可以调用jQuery.contains(a,b),也就是Sizzle.contains(a,b),检查匹配元素是否包含了可以匹配参数target的子元素。 // 定义方法.has(target),参数target可以是选择器表达式、jQuery对象及元素。 has: function (target) { // 构造匹配参数target的jQuery对象。 var targets = jQuery(target, this), l = targets.length; // 调用方法.filter(selector)遍历当前匹配元素集合,检查每个匹配元素是否包含了参数target所匹配的某个元素,如果包含则保留,不包含则丢弃。 return this.filter(function () { var i = 0; for (; i < l; i++) { if (jQuery.contains(this, targets[i])) { return true; } } }); }, // 方法.closest(selectors,context)用于在当前元素集合和它们的祖先元素中查找与参数selectors匹配的最近元素,并用检查结果构造一个新jQuery对象。 // 方法.closest(selectors,context)与.parents(selector)的行为相似,都是沿着DOM树向上查找匹配元素。 // 注意: //.closest(selectors,context),从每个匹配元素开始,沿着DOM树向上遍历,直到找到匹配参数selectors的元素 //.parents(selector),从每个匹配元素的父元素开始,沿着DOM树向上遍历,查到所有与参数selectors匹配的元素 closest: function (selectors, context) { // 参数selectors:用于匹配DOM元素,可选值有:选择器表达式、jQuery对象、DOM元素、数组 // 参数context:可选的上下文,用于限定查找范围 var cur, i = 0, l = this.length, matched = [], targets = typeof selectors !== "string" && jQuery(selectors); // Positional selectors never match, since there's no _selection_ context // 位置选择器永远不会匹配,因为没有_selection_上下文 if (!rneedsContext.test(selectors)) { for (; i < l; i++) { // 如果当前元素cur不匹配参数context,则读取它的父元素,继续向上遍历。如果父元素不存在,或者父元素不在文档中,或者父元素是上下文,或者父元素是文档片段,则跳出循环,继续在下一个匹配元素和它的祖先元素中查找。 for (cur = this[i]; cur && cur !== context; cur = cur.parentNode) { // Always skip document fragments // 总是跳过文档片段 if (cur.nodeType < 11 && (targets ? targets.index(cur) > -1 : // Don't pass non-elements to Sizzle // 不要将非元素传递给Sizzle cur.nodeType === 1 && jQuery.find.matchesSelector(cur, selectors))) { matched.push(cur); break; } } } } // 如果找到了多个匹配参数selectors的元素,则调用方法jQuery.unique(results),也就是Sizzle.uniqueSort(results),执行排序和去重效果。 // 最后用找到的元素构造一个新jQuery对象,并返回 return this.pushStack(matched.length > 1 ? jQuery.uniqueSort(matched) : matched); }, // Determine the position of an element within the set // 方法.index(elem)用于判断元素在元素集合中的下标位置。该方法的行为随参数elem的不同而不同。 index: function (elem) { // No argument, return index in parent // 没有传入参数,返回当前匹配元素集合中第一个元素相对其兄弟元素的下标位置。如果第一个元素没有父元素,则返回-1. if (!elem) { return (this[0] && this[0].parentNode) ? this.first().prevAll().length : -1; } // Index in selector // 如果参数elem是字符串,则调用构造函数jQuery(elem)查找参数elem匹配的元素集合,然后调用方法indexOf.call()获取当前匹配元素集合中第一个元素在查找结果中的位置,并返回。 if (typeof elem === "string") { return indexOf.call(jQuery(elem), this[0]); } // Locate the position of the desired element // 定位所需元素的位置 // 如果参数elem是DOM元素,则调用方法indexOf.call()获取参数elem在当前元素匹配集合中的位置,并返回;如果参数elem是jQuery对象,则调用方法indexOf.call()获取参数elem的第一个元素在当前匹配元素集合中的位置,并返回。 return indexOf.call(this, // If it receives a jQuery object, the first element is used // 如果它接收到jQuery对象,则使用第一个元素 elem.jquery ? elem[0] : elem ); }, // 方法.add(selector, context)用当前jQuery对象中的元素和传入的参数构造一个新jQuery对象。构造函数jQuery()可以接受的参数格式,该方法都可以接受。另外,该方法不会改变当前jQuery对象。 add: function (selector, context) { // 构造jQuery对象。 return this.pushStack( // 调用方法jQuery.uniqueSort()也就是Sizzle.unqueSort()对合并后的元素进行排序和去抽 jQuery.uniqueSort( // 调用方法jQuery.merge(first, second)将当前jQuery对象中的元素和元素集合get的元素合并 jQuery.merge(this.get(), jQuery(selector, context)) ) ); }, // 方法.addBack()用于将之前匹配的元素加入到当前匹配的元素中,并以新的jQuery对象的形式返回。通过调用add方法实现。 addBack: function (selector) { return this.add(selector == null ? this.prevObject : this.prevObject.filter(selector) ); } }); function sibling( cur, dir ) { while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} return cur; } jQuery.each( { parent: function( elem ) { var parent = elem.parentNode; return parent && parent.nodeType !== 11 ? parent : null; }, parents: function( elem ) { return dir( elem, "parentNode" ); }, parentsUntil: function( elem, i, until ) { return dir( elem, "parentNode", until ); }, next: function( elem ) { return sibling( elem, "nextSibling" ); }, prev: function( elem ) { return sibling( elem, "previousSibling" ); }, nextAll: function( elem ) { return dir( elem, "nextSibling" ); }, prevAll: function( elem ) { return dir( elem, "previousSibling" ); }, nextUntil: function( elem, i, until ) { return dir( elem, "nextSibling", until ); }, prevUntil: function( elem, i, until ) { return dir( elem, "previousSibling", until ); }, siblings: function( elem ) { return siblings( ( elem.parentNode || {} ).firstChild, elem ); }, children: function( elem ) { return siblings( elem.firstChild ); }, contents: function( elem ) { if ( nodeName( elem, "iframe" ) ) { return elem.contentDocument; } // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only // Treat the template element as a regular one in browsers that // don't support it. if ( nodeName( elem, "template" ) ) { elem = elem.content || elem; } return jQuery.merge( [], elem.childNodes ); } }, function( name, fn ) { jQuery.fn[ name ] = function( until, selector ) { var matched = jQuery.map( this, fn, until ); if ( name.slice( -5 ) !== "Until" ) { selector = until; } if ( selector && typeof selector === "string" ) { matched = jQuery.filter( selector, matched ); } if ( this.length > 1 ) { // Remove duplicates if ( !guaranteedUnique[ name ] ) { jQuery.uniqueSort( matched ); } // Reverse order for parents* and prev-derivatives if ( rparentsprev.test( name ) ) { matched.reverse(); } } return this.pushStack( matched ); }; } );

浙公网安备 33010602011771号