首先使用原生语句查找,其次在context下找到所有节点元素,校验元素是否匹配选择器,进行过滤,获取到待查询的元素。
过滤时分为两种情形,针对选择器是否带:first()等位置关系伪类,若携带,setMatcher函数得到过滤函数,:first()伪类的校验函数也通过引用对象的形式“滤除“未匹配的节点;若不携带,elementMatcher得到校验函数,其中层级关系如“#form > .input”,通过addCombinator函数包装“#form“的校验函数,根据选择器">"待校验节点和待过滤节elem的位置关系得到待校验节点,同样赋值给elem(以便传递更上层的addCombinator包装后的校验函数)。该addCombinator包装后的校验函数和".input”校验函数平级,通过elementMatcher函数实现自右向左的校验,为传递elem的方便。
/*! * Sizzle CSS Selector Engine v2.3.0 * https://sizzlejs.com/ * * Copyright jQuery Foundation and other contributors * Released under the MIT license * http://jquery.org/license * * Date: 2016-01-04 */ (function( window ) { var i, support, Expr, getText, isXML,// 文档根节点是否xml tokenize, compile, select, outermostContext, sortInput, hasDuplicate,// 用于判断两元素是否相同 // Local document vars setDocument, document, docElem, documentIsHTML,// 文档根节点是否html rbuggyQSA,// 存储querySelectorAll方法不支持的查询字符串 rbuggyMatches,// 存储matchesSelector方法不支持的查询字符串 matches,// 存储各浏览器的matchesSelector方法 contains, // Instance-specific data expando="sizzle"+1*new Date(),// sizzle标识 preferredDoc=window.document, dirruns=0, done=0, // 缓存是否匹配样式的函数,[]属性取出,()方法添加缓存 classCache=createCache(), // 将选择器通过正则转化为对象形式(matched匹配字符、type类型、matches拆分匹配字符)存储 tokenCache=createCache(), // compilerCache存储校验函数 compilerCache=createCache(), sortOrder=function(a,b){ if ( a===b ){ hasDuplicate=true; } return 0; }, hasOwn=({}).hasOwnProperty, arr=[], pop=arr.pop, push_native=arr.push, push=arr.push, slice=arr.slice, 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", // 匹配空格 whitespace="[\\x20\\t\\r\\n\\f]", // 非获取匹配\.或A-Za-z0-9或unicode字符串 identifier="(?:\\\\.|[\\w-]|[^\0-\\xa0])+", // 匹配[attrName='attrVal'],匹配match[3|4|5]合并为check attributes="\\["+whitespace+"*("+identifier+")(?:"+whitespace+"*([*^$|!~]?=)"+whitespace+ // 单引号包裹\.、非\',或双引号包裹\.、非\",或identifier "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+identifier+"))|)"+whitespace+ "*\\]", pseudos=":("+identifier+")(?:\\(("+ "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|"+// 引号部分 "((?:\\\\.|[^\\\\()[\\]]|"+attributes+")*)|"+// 使用js滤过部分 ".*"+// tokenize函数获取")"前内容进行选择,")"移除 ")\\)|)", // 匹配空格 rwhitespace=new RegExp(whitespace+"+","g"), // 匹配首尾端的空格,(?:)非获取匹配 rtrim=new RegExp("^"+whitespace+"+|((?:^|[^\\\\])(?:\\\\.)*)"+whitespace+"+$","g"), // 选择器以空格、逗号起始,*匹配0个或多个 rcomma=new RegExp("^"+whitespace+"*,"+whitespace+"*"), // 选择器以空格、>、+、~起始,*匹配0个或多个 rcombinators=new RegExp("^"+whitespace+"*([>+~]|"+whitespace+")"+whitespace+"*"), // 获取属性的值 rattributeQuotes=new RegExp("="+whitespace+"*([^\\]'\"]*?)"+whitespace+"*\\]","g"), rpseudo=new RegExp(pseudos), ridentifier=new RegExp("^"+identifier+"$"), 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"),// 参Expr.prefilter.CHILD "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")// 匹配eq(1) }, rinputs=/^(?:input|select|textarea|button)$/i,// 匹配input|select|textarea|button rheader=/^h\d$/i,// 匹配h1、h2等 rnative=/^[^{]+\{\s*\[native \w/,// 判断是否浏览器原生函数 rquickExpr=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,// 匹配id、tag、class选择器 rsibling=/[+~]/, // CSS escapes // 选择器unicode转义 runescape=new RegExp("\\\\([\\da-f]{1,6}"+whitespace+"?|("+whitespace+")|.)","ig"), funescape=function(_,escaped,escapedWhitespace){ var high="0x"+escaped-0x10000; return high!==high || escapedWhitespace ? escaped : high<0 ? String.fromCharCode(high+0x10000) : String.fromCharCode(high>>10 | 0xD800, high & 0x3FF | 0xDC00); }, // CSS string/identifier serialization 转义 // https://drafts.csswg.org/cssom/#common-serializing-idioms rcssescape=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g, fcssescape=function(ch,asCodePoint){ if ( asCodePoint ){ // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER 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 return "\\"+ch; }, // 用户离开文档时,重新调用setDocument函数设置document为sizzle.js所在文档根节点 unloadHandler=function(){ setDocument(); }, disabledAncestor=addCombinator( function( elem ) { return elem.disabled === true; }, { dir: "parentNode", next: "legend" } ); // push方法拼接两个数组 try{ push.apply( (arr=slice.call(preferredDoc.childNodes)), preferredDoc.childNodes ); arr[preferredDoc.childNodes.length].nodeType; }catch(e){ push={apply:arr.length ? function(target,els){ push_native.apply(target,slice.call(els)); } : function(target,els){ var j=target.length, i=0; while ( (target[j++]=els[i++]) ){} target.length=j-1; } }; } // 参数seed在matchs方法中使用,用以判断元素 function Sizzle(selector,context,results,seed){ var m, i, elem, nid, match, groups, newSelector, newContext=context && context.ownerDocument, nodeType=context ? context.nodeType : 9; results=results || []; // selector不是字符串,或context不是节点时,以results作为返回值 if ( typeof selector!=="string" || !selector || nodeType!==1 && nodeType!==9 && nodeType!==11 ){ return results; } // 调用getElementBy["*"]或querySelectorAll方法快速查找元素 if ( !seed ){ if ( (context ? context.ownerDocument || context : preferredDoc)!==document ){ setDocument(context); } context=context || document; if ( documentIsHTML ){ // nodeType===11,DocumentFragment节点没有getElementBy[*]方法 if ( nodeType!==11 && (match=rquickExpr.exec(selector)) ){ // ID选择器快速查找 if ( (m=match[1]) ){ if ( nodeType===9 ){// 文档节点 if ( (elem=context.getElementById(m)) ){ if ( elem.id === m ){ results.push(elem); return results; } }else{ return results; } }else{// 普通节点 if ( newContext && (elem= newContext.getElementById(m)) && contains(context,elem) && elem.id===m ){ results.push(elem); return results; } } // Tag选择器快速查找 }else if(match[2]){ push.apply(results,context.getElementsByTagName(selector)); return results; // Class选择器快速查找 }else if( (m=match[3]) && support.getElementsByClassName && context.getElementsByClassName ){ push.apply(results,context.getElementsByClassName(m)); return results; } } if ( support.qsa && !compilerCache[selector+" "] && (!rbuggyQSA || !rbuggyQSA.test(selector)) ){ if ( nodeType!==1 ){ newContext=context; newSelector=selector; // Support: IE <=8 qSA方法将在context外查找元素,需重设selector,除了object节点 }else if( context.nodeName.toLowerCase()!=="object" ){ if ( (nid=context.getAttribute("id")) ){ nid=nid.replace(rcssescape,fcssescape); }else{ context.setAttribute("id",(nid=expando)); } // 将selector通过正则表达式拆解成对象形式存储到tokenCache中 groups=tokenize(selector); i=groups.length; while ( i-- ){ // toSelector获取逗号前后分割的单一选择器 groups[i]="#"+nid+" "+toSelector(groups[i]); } newSelector=groups.join(","); // testContext(context.parentNode)返回父节点 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"); } } } } } } // 不能调用getElementBy["*"]或querySelectorAll方法的,调用select函数查找 return select(selector.replace(rtrim,"$1"),context,results,seed); } // 创建key-value缓存,存储在创建缓存的cache函数里,keys通过闭包维持存在 function createCache(){ var keys=[]; function cache(key,value){ if ( keys.push(key+" ")>Expr.cacheLength ){// Expr.cacheLength默认50 delete cache[keys.shift()]; } return (cache[key+" "]=value); } return cache; } // 向传参fn函数添加标记,以便sizzle进行移除、修改、查找处理 function markFunction(fn){ fn[expando]=true; return fn; } // 创建fieldset元素,执行fn函数,用来实现浏览器能力检测 function assert(fn){ var el=document.createElement("fieldset"); try{ return !!fn(el); }catch(e){ return false; }finally{ if ( el.parentNode ){ el.parentNode.removeChild(el); } el=null; } } // 为Expr.attrHandle添加方法,针对浏览器兼容性问题,添加特殊的获取属性方法 function addHandle(attrs,handler){ var arr=attrs.split("|"), i=arr.length; while ( i-- ){ Expr.attrHandle[arr[i]]=handler; } } // 两元素相邻状况,返回-1时b为a后续的兄弟节点,1为其他情况 function siblingCheck(a,b){ var cur=b && a, diff=cur && a.nodeType===1 && b.nodeType===1 && a.sourceIndex-b.sourceIndex; // 使用IE的sourceIndex方法判断两元素是否相邻 if ( diff ){ return diff; } // Check if b follows a if ( cur ){ while ( (cur=cur.nextSibling) ){ if ( cur===b ){ return -1; } } } return a ? 1 : -1; } // 创建:radio、:checkbox、:file、:password、:image伪类选择器,返回函数形式 function createInputPseudo(type){ return function(elem){ var name=elem.nodeName.toLowerCase(); return name==="input" && elem.type===type; }; } // 创建:submit、:reset伪类选择器,返回函数形式 function createButtonPseudo(type){ return function(elem){ var name=elem.nodeName.toLowerCase(); return (name==="input" || name==="button") && elem.type===type; }; } // 判断表单元素是否可用 function createDisabledPseudo(disabled){ // Known :disabled false positives: // IE: *[disabled]:not(button, input, select, textarea, optgroup, option, menuitem, fieldset) // not IE: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable return function(elem){ // Check form elements and option elements for explicit disabling return "label" in elem && elem.disabled===disabled || "form" in elem && elem.disabled===disabled || // Check non-disabled form elements for fieldset[disabled] ancestors "form" in elem && elem.disabled===false && ( // Support: IE6-11+ // Ancestry is covered for us elem.isDisabled===disabled || // Otherwise, assume any non-<option> under fieldset[disabled] is disabled /* jshint -W018 */ elem.isDisabled!==!disabled && ("label" in elem || !disabledAncestor(elem))!==disabled ); }; } // 由fn获得匹配元素的index值,再将seed中未匹配的项置为false,matches添加匹配的项 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]); } } }); }); } // context有getElementsByTagName方法,返回context function testContext(context){ return context && typeof context.getElementsByTagName!=="undefined" && context; } // 浏览器能力检测 support=Sizzle.support={}; // 是否xml,通过判断根节点是否html isXML=Sizzle.isXML=function(elem){ var documentElement=elem && (elem.ownerDocument || elem).documentElement; return documentElement ? documentElement.nodeName!=="HTML" : false; }; // 返回当前文档根节点document,浏览器能力检测 setDocument=Sizzle.setDocument=function(node){ var hasCompare, subWindow, doc=node ? node.ownerDocument || node : preferredDoc; // doc是当前的文档根节点或者无效的节点 if ( doc===document || doc.nodeType!==9 || !doc.documentElement ){ return document; } document=doc; docElem=document.documentElement; documentIsHTML=!isXML(document); // Support: IE 9-11, Edge // 用户离开文档时,重新调用setDocument函数设置document为sizzle.js所在文档根节点 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); } } // 浏览器能力getElementBy[*]检测,Expr.find|filter["*"]方法改写 // Support: IE<8 校验getAttribute方法能否获取元素的属性 support.attributes=assert(function(el){ el.className="i"; return !el.getAttribute("className"); }); // 校验getElementsByTagName("*")是否会返回注释节点 support.getElementsByTagName=assert(function(el){ el.appendChild(document.createComment("")); return !el.getElementsByTagName("*").length; }); // Support: IE<9 函数体内含有native code support.getElementsByClassName=rnative.test(document.getElementsByClassName); // Support: IE<10 getElementById方法是否包含name属性相同的元素,用getElementsByName校验 support.getById=assert(function(el){ docElem.appendChild(el).id=expando; return !document.getElementsByName || !document.getElementsByName(expando).length; }); if ( support.getById ){ Expr.find["ID"]=function(id,context){ if ( typeof context.getElementById!=="undefined" && documentIsHTML ){ var m=context.getElementById(id); return m ? [m] : []; } }; Expr.filter["ID"]=function(id){ var attrId=id.replace(runescape,funescape); return function(elem){ return elem.getAttribute("id")===attrId; }; }; }else{ // Support: IE6/7 getElementById兼容性问题 delete Expr.find["ID"]; 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; }; }; } Expr.find["TAG"]=support.getElementsByTagName ? function(tag,context){ if ( typeof context.getElementsByTagName!=="undefined" ){ return context.getElementsByTagName(tag); }else if( support.qsa ){ return context.querySelectorAll(tag); } } : function(tag,context){ var elem, tmp=[], i=0, results=context.getElementsByTagName(tag); if ( tag==="*" ){// 移除注释节点 while ( (elem=results[i++]) ){ if ( elem.nodeType===1 ){ tmp.push(elem); } } return tmp; } return results; }; Expr.find["CLASS"]=support.getElementsByClassName && function(className,context){ if ( typeof context.getElementsByClassName!=="undefined" && documentIsHTML ){ return context.getElementsByClassName(className); } }; // 浏览器querySelectorAll、matchesSelector能力检测 // matchesSelector方法能力检测,不支持的查询字符串填入rbuggyMatches rbuggyMatches=[]; // 各浏览器querySelectorAll方法能力检测,不支持的查询字符串填入rbuggyQSA rbuggyQSA=[]; if ( (support.qsa=rnative.test(document.querySelectorAll)) ){ assert(function(el){ docElem.appendChild(el).innerHTML="<a id='"+expando+"'></a>"+ "<select id='"+expando+"-\r\\' msallowcapture=''>"+ "<option selected=''></option></select>"; // Support: IE8, Opera 11-12.16 属性内容为空时理应找不到元素 if ( el.querySelectorAll("[msallowcapture^='']").length ){ rbuggyQSA.push("[*^$]="+whitespace+"*(?:''|\"\")"); } // Support: IE8 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("~=");// ~=元素的属性值中含有用空格分割的value } // IE8报错 if ( !el.querySelectorAll(":checked").length ){ rbuggyQSA.push(":checked"); } // Support: Safari 8+, iOS 8+ if ( !el.querySelectorAll("a#"+expando+"+*").length ){ rbuggyQSA.push(".#.+[+~]"); } }); assert(function(el){ el.innerHTML="<a href='' disabled='disabled'></a>" + "<select disabled='disabled'><option/></select>"; var input=document.createElement("input"); input.setAttribute("type","hidden"); el.appendChild(input).setAttribute("name","D"); // Support: IE8 if ( el.querySelectorAll("[name=d]").length ){ rbuggyQSA.push("name"+whitespace+"*[*^$|!~]?="); } // FF 3.5 - 隐藏的元素状态为enabled;IE8报错 if ( el.querySelectorAll(":enabled").length!==2 ){ rbuggyQSA.push(":enabled",":disabled"); } // Support: IE9-11+ 状态为disabled的元素其子节点为disabled时,将不包含该子节点 docElem.appendChild(el).disabled=true; if ( el.querySelectorAll(":disabled").length!==2 ){ rbuggyQSA.push(":enabled",":disabled"); } // 无效的伪类本当报错,不报错的时候,rbuggyQSA添加",.*:" el.querySelectorAll("*,:x"); rbuggyQSA.push(",.*:"); }); } if ( (support.matchesSelector=rnative.test( (matches=docElem.matches || docElem.webkitMatchesSelector || docElem.mozMatchesSelector || docElem.oMatchesSelector || docElem.msMatchesSelector) )) ){ assert(function( el ) { // IE 9 未插入文档的节点是否适用matchesSelector support.disconnectedMatch=matches.call(el,"*"); matches.call(el,"[s!='']:x"); rbuggyMatches.push("!=",pseudos); }); } rbuggyQSA=rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); rbuggyMatches=rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); // 判断a元素是否包含b元素 hasCompare=rnative.test(docElem.compareDocumentPosition); 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) : // compareDocumentPosition方法判断元素的相对位置 // a & 16 按位与,当a大于等于16为16,其他为0 a.compareDocumentPosition && a.compareDocumentPosition(bup) & 16 )); } : function(a,b){ if (b){ while ( (b=b.parentNode) ){ if ( b===a ){ return true; } } } return false; }; // 判断a、b元素的位置关系 sortOrder=hasCompare ? function(a,b){ if ( a===b ){ hasDuplicate=true; return 0; } // 其中一个有compareDocumentPosition方法,一个没有 var compare=!a.compareDocumentPosition-!b.compareDocumentPosition; if ( compare ){ return compare; } compare=(a.ownerDocument || a)===(b.ownerDocument || b) ? a.compareDocumentPosition(b) : 1;// 不在同一个文档 // 不相关的节点,a在当前文档中返回-1,b在当前文档中返回1 if ( compare & 1 || (!support.sortDetached && b.compareDocumentPosition(a)===compare) ){ 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){ 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 if ( !aup || !bup ){ return a===document ? -1 : b===document ? 1 : aup ? -1 : bup ? 1 : sortInput ? ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : 0; // If the nodes are siblings, we can do a quick check }else if( aup===bup ){ return siblingCheck(a,b); } // Otherwise we need full lists of their ancestors for comparison 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中不满足expr的元素 Sizzle.matches=function(expr,elements){ return Sizzle(expr,null,null,elements); }; // 检查elem是否expr选择器,原生语句matchesSelector()优先 Sizzle.matchesSelector=function(elem,expr){ if ( (elem.ownerDocument || elem)!==document ){ setDocument(elem); } expr=expr.replace(rattributeQuotes,"='$1']"); 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 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=function(context,elem){ if ( (context.ownerDocument || context)!==document ){ setDocument(context); } return contains(context,elem); }; // 有兼容性问题的个别属性通过Expr.attrHandle[name.toLowerCase()]方法获取 // 当前浏览器支持getAttribute方法或者xml文档的时候,通过getAttribute方法获取 // 其他通过getAttributeNode(name).specified获取属性值 Sizzle.attr=function(elem,name){ if ( (elem.ownerDocument || elem)!==document ){ setDocument(elem); } var fn=Expr.attrHandle[name.toLowerCase()], val=fn && hasOwn.call(Expr.attrHandle,name.toLowerCase()) ? fn(elem,name,!documentIsHTML) : undefined; return val!==undefined ? val : support.attributes || !documentIsHTML ? elem.getAttribute(name) : (val=elem.getAttributeNode(name)) && val.specified ? val.value : null; }; // unicode转义 Sizzle.escape=function(sel){ return (sel+"").replace(rcssescape,fcssescape); }; // 报错 Sizzle.error=function(msg){ throw new Error("Syntax error, unrecognized expression: "+msg); }; // support.detectDuplicates为否时去重,为真时保留重复项 Sizzle.uniqueSort=function(results){ var elem, duplicates=[], j=0, i=0; hasDuplicate=!support.detectDuplicates; sortInput=!support.sortStable && results.slice(0); results.sort(sortOrder); if ( hasDuplicate ){ // 比对前后两个数组项是否相同,相同记录前一项的index,splice方法移除该项 while ( (elem=results[i++]) ){ if ( elem===results[i] ){ j=duplicates.push(i); } } while ( j-- ){ results.splice(duplicates[j],1); } } sortInput=null; return results; }; // 调用textContent属性或者拼接字符串的方式获取节点及子节点的文本内容 getText=Sizzle.getText=function(elem){ var node, ret="", i=0, nodeType=elem.nodeType; if ( !nodeType ){ while ( (node=elem[i++]) ){ ret+=getText(node); } }else if( nodeType===1 || nodeType===9 || nodeType===11 ){ if ( typeof elem.textContent==="string" ){ return elem.textContent; }else{ for( elem=elem.firstChild; elem; elem=elem.nextSibling ){ ret+=getText(elem); } } }else if( nodeType===3 || nodeType===4 ){ return elem.nodeValue; } return ret; }; Expr=Sizzle.selectors={ cacheLength:50,// 缓存默认长度 createPseudo: markFunction,// fn添加属性,以便查找及管理 match:matchExpr,// 正则表达式 attrHandle:{},// 针对浏览器兼容性问题,添加特殊的获取属性方法,如value、disabled等 find:{}, // dir待校验元素与自右向左查询元素的相对关系,first为真值,严格校验模式 // 如">",须严格确保父子关系 relative:{ ">":{dir:"parentNode",first:true}, " ":{dir:"parentNode"}, "+":{dir:"previousSibling",first:true}, "~":{dir:"previousSibling"} }, // 处理匹配的正则数组,同时将正则表达式不完善的地方补全 preFilter:{ "ATTR":function(match){ // match[1] Expr.filter.ATTR中参数name // match[2] Expr.filter.ATTR中参数operator // match[3] Expr.filter.ATTR中参数check match[1]=match[1].replace(runescape,funescape); 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){ // match[1] Expr.filter.ChILD中参数type 如nth // match[2] Expr.filter.ChILD中参数what 如child // match[3] Expr.filter.ChILD中参数argument 如3n+2 // match[4] Expr.filter.ChILD中参数first 如3 // match[5] Expr.filter.ChILD中参数last 如2 /* matches from matchExpr["CHILD"] 1 type (only|nth|...) 2 what (child|of-type) 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) 如+3n-2 4 xn-component of xn+y argument ([+-]?\d*n|) 如+3n 5 sign of xn-component 如+ 6 x of xn-component 如3 7 sign of y-component 如- 8 y of y-component 如2 */ match[1]=match[1].toLowerCase(); if ( match[1].slice(0,3)==="nth" ){ if ( !match[3] ){ Sizzle.error(match[0]); } 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" ); }else if( match[3] ){ Sizzle.error(match[0]); } return match; }, "PSEUDO":function(match){ // match[1] Expr.filter.PSEUDO中参数pesudo,Expr.pseudos中方法名 如not // match[2] Expr.filter.PSEUDO中参数argument,Expr.pseudos["*"]方法的参数 如".btn" /* matches from matchExpr["PSEUDO"] not(".btn") 1 pesudo (not|eq|gt|hidden) 2 argument ('*'|"*"|\d|...) 如".btn" 3 quotes ('*'|"*") 如".btn" 4 single-quote ('') 5 double-quote ("") 如".btn" 6 使用js语句进行过滤,不匹配 */ var excess, unquoted=!match[6] && match[2]; if ( matchExpr["CHILD"].test(match[0]) ){// 作为child选择器处理 return null; } if ( match[3] ){ match[2]=match[4] || match[5] || ""; }else if( unquoted && rpseudo.test(unquoted) && (excess=tokenize(unquoted,true)) && (excess=unquoted.indexOf(")",unquoted.length-excess)-unquoted.length) ){ match[0]=match[0].slice(0,excess);// 移除")"后字符串 match[2]=unquoted.slice(0,excess); } return match.slice(0,3); } }, // 匹配tag、class、attr、child、pseudo的方法,验证元素是否匹配,返回值为校验函数 filter:{ "TAG":function(nodeNameSelector){ var nodeName=nodeNameSelector.replace(runescape,funescape).toLowerCase(); return nodeNameSelector==="*" ? function(){return true;} : function(elem){ return elem.nodeName && elem.nodeName.toLowerCase()===nodeName; }; }, "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){// 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(3n+2) type为nth,what为child,argument为3n+2,first为3,last为2 "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 ? // :nth-child(1)、:nth-type-of(1) 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,// 非xml文档,nth-child选择器,缓存 diff=false; if ( parent ){ // :(first|last|only)-(child|of-type) if ( simple ){ // simple限制为真值,dir随last-child变为nextSibling // first-child之前有元素返回false,last-child判断其后有无元素 while ( dir ){ node=elem; while ( (node=node[dir]) ){ if ( ofType ? node.nodeName.toLowerCase()===name : node.nodeType===1 ){ return false; } } // only-child时,dir由previousSibling反转为nextSibling start=dir=type==="only" && !start && "nextSibling"; } return true; } start=[forward ? parent.firstChild : parent.lastChild]; // 不是xml文档、nth-child选择器条件下使用缓存 if ( forward && useCache ){ node=parent; outerCache=node[expando] || (node[expando]={}); // uniqueCache缓存当前元素的序号diff,nodeIndex包含其他节点元素形式 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] || // 没有缓存时初始设置diff=nodeIndex=0,node=parent.firstChild (diff=nodeIndex=0) || start.pop()) ){ if ( node.nodeType===1 && ++diff && node===elem ){ uniqueCache[type]=[dirruns,nodeIndex,diff]; break; } } }else{ if ( useCache ){ node=elem; outerCache=node[expando] || (node[expando]={}); 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 ){ while ( (node=++nodeIndex && node && node[dir] || (diff=nodeIndex=0) || start.pop()) ){ if ( ( ofType ? node.nodeName.toLowerCase()===name :// 标签相同的元素 node.nodeType===1 ) && ++diff ){ if ( useCache ){ outerCache=node[expando] || (node[expando]={}); uniqueCache=outerCache[node.uniqueID] || (outerCache[node.uniqueID]={}); uniqueCache[type]=[dirruns,diff]; } if ( node===elem ){ break; } } } } } diff-=last;// 节点的序号diff return diff===first || (diff%first===0 && diff/first>=0); } }; }, "PSEUDO":function(pseudo,argument){ var args, // 获取伪类校验函数,Expr.pseudos优先级高于Expr.setFilters // Expr.setFilters的原型为Expr.pseudos fn=Expr.pseudos[pseudo] || Expr.setFilters[pseudo.toLowerCase()] || Sizzle.error("unsupported pseudo: "+pseudo); // pesudo如not、has等通过createPseudo创建校验函数fn、或first等通过createPositionalPseudo创建 // 校验函数需要配置参数方可用于校验元素 if ( fn[expando] ){ return fn(argument); } // But maintain support for old signatures // jquery原先的版本用length代替expando实现其功能??? if ( fn.length>1 ) { args=[pseudo,pseudo,"",argument]; // pseudo为Expr.setFilters实例自有的方法名,参数为待过滤元素和argument // 使用引用对象形式过滤,而不是返回过滤结果 // 传参seed待过滤元素,函数执行完成seed中匹配项设为false 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:{ // :not('.classname')校验元素不是.classname元素 "not":markFunction(function(selector){ var input=[], results=[], // 通过compile获取:not()内选择器的校验函数 matcher=compile(selector.replace(rtrim,"$1")); return matcher[expando] ? // :not()内选择器为:first等 markFunction(function(seed,matches,context,xml){ var elem, unmatched=matcher(seed,null,xml,[]), i=seed.length; while ( i-- ){ if ( (elem=unmatched[i]) ){ seed[i]=!(matches[i]=elem);// 匹配matcher的设为否值 } } }) : function(elem,context,xml){ input[0]=elem; matcher(input,null,xml,results); input[0]=null; return !results.pop(); }; }), // 使用elementMatchers普通校验函数,返回布尔值 // 首先由Expr.filter.PESUDO方法获得selector参数,再使用返回函数校验elem "has":markFunction(function(selector){ return function(elem){ return Sizzle(selector,elem).length>0; }; }), // 是否包含给定文本 "contains":markFunction(function(text){ text=text.replace(runescape,funescape); return function(elem){ return ( elem.textContent || elem.innerText || getText(elem) ).indexOf(text)>-1; }; }), // :lang(en)校验<div lang="en"/>或<div lang="en-us"/> "lang":markFunction(function(lang){ 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; }; }), // 使用elementMatchers普通校验函数,返回布尔值 // 以当前页面hash为id属性的节点 "target":function(elem){ var hash=window.location && window.location.hash;// http://example.com/#foo情形为foo return hash && hash.slice(1)===elem.id; }, // 根节点 "root":function(elem){ return elem===docElem; }, "focus":function(elem){ return elem===document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); }, "enabled":createDisabledPseudo(false), "disabled":createDisabledPseudo(true), // 选中的option或radio、checkbox "checked":function(elem){ // In CSS3, :checked should return both checked and selected elements // 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); }, // 选中的option "selected":function(elem){ // Accessing this property makes selected-by-default // options in Safari work properly if ( elem.parentNode ) { elem.parentNode.selectedIndex; } return elem.selected===true; }, // 元素是否没有子节点 "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":function(elem){ return !Expr.pseudos["empty"](elem); }, // h1、h2等 "header":function(elem){ return rheader.test(elem.nodeName); }, // 表单元素select、input、button、textarea "input":function(elem){ return rinputs.test(elem.nodeName); }, "button":function(elem){ var name=elem.nodeName.toLowerCase(); return name==="input" && elem.type==="button" || name==="button"; }, // 输入框 "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" ); }, // 匹配index值为0的元素,setMatchers特殊校验函数中传入待过滤元素,index=0的满足条件 "first":createPositionalPseudo(function(){ return [0]; }), "last":createPositionalPseudo(function(matchIndexes,length){ return [length-1]; }), "eq":createPositionalPseudo(function(matchIndexes,length,argument){ return [argument<0 ? argument+length : argument]; }), // index为偶数的元素 "even":createPositionalPseudo(function(matchIndexes,length){ var i=0; for ( ; i<length; i+=2 ){ matchIndexes.push(i); } return matchIndexes; }), "odd":createPositionalPseudo(function(matchIndexes,length){ var i = 1; for ( ; i<length; i+=2 ){ matchIndexes.push(i); } return matchIndexes; }), // :lt(3) index值小于3的元素 "lt":createPositionalPseudo(function(matchIndexes,length,argument){ var i = argument < 0 ? argument + length : argument; for ( ; --i >= 0; ) { matchIndexes.push( i ); } return matchIndexes; }), "gt":createPositionalPseudo(function(matchIndexes,length,argument){ var i=argument<0 ? argument+length : argument; for ( ; ++i<length; ) { matchIndexes.push(i); } return matchIndexes; }) } }; Expr.pseudos["nth"]=Expr.pseudos["eq"]; // 创建radio、checkbox、file、password、image伪类选择器 for ( i in {radio:true, checkbox:true, file:true, password:true, image:true} ){ Expr.pseudos[i]=createInputPseudo(i); } // 创建:submit、:reset伪类选择器 for ( i in {submit:true, reset:true} ){ Expr.pseudos[i]=createButtonPseudo(i); } // 对外提供添加Expr.pseudos的接口,Expr.pseudos优先级高于Expr.setFilters function setFilters(){} setFilters.prototype=Expr.filters=Expr.pseudos; Expr.setFilters=new setFilters(); // parseOnly为真输出未匹配字符串的长度,否则报错或者添加缓存tokenCache // .className,[attr] 将被拆解为两个tokens存储在groups中,表示两个选择器 // .className > [attr] 将被拆解为一个tokens存储在groups中 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 ){ // 选择器以空格+逗号起始,移除该空格+逗号 if ( !matched || (match=rcomma.exec(soFar)) ){ if ( match ){ soFar=soFar.slice(match[0].length) || soFar; } groups.push((tokens=[])); } matched=false; // 选择器以空格、>、+、~起始,*匹配0个或多个 // tokens中value正则匹配的数组,type类型如>或+或~ if ( (match=rcombinators.exec(soFar)) ){ matched=match.shift(); tokens.push({ value:matched, type:match[0].replace(rtrim," ") }); soFar=soFar.slice(matched.length); } for ( type in Expr.filter ){// type为tag、class、attr、child或pseudo if ( (match=matchExpr[type].exec(soFar)) && (!preFilters[type] || (match=preFilters[type](match))) ){ matched=match.shift(); tokens.push({ value:matched,// 匹配的完整字符串 type:type,// 类型如tag、class、attr、child或pseudo matches:match// 匹配字符串拆分的数组,如Attr将被拆分为name、operator、check }); soFar=soFar.slice(matched.length); } } if ( !matched ){ break; } } // parseOnly为真时,返回未匹配字符串的长度,否则报错或者tokenCache添加处理后的匹配内容tokens return parseOnly ? soFar.length : soFar ? Sizzle.error(selector) : tokenCache(selector,groups).slice(0); }; // 获取逗号前后分割的单一选择器 function toSelector(tokens){ var i=0, len=tokens.length, selector=""; for ( ; i<len; i++ ){ selector+=tokens[i].value; } return selector; } // 包装matcher,返回校验函数,用以校验特定的elem,按情况启动循环及缓存 // 缓存放置在待校验元素的父节点上 // combinator配置待检测元素和elem的位置关系,以及检测标准,严格父子临近关系或其他 function addCombinator(matcher,combinator,base){ var dir=combinator.dir, skip=combinator.next,// 跳过校验的元素nodeName key=skip || dir, checkNonElements=base && key==="parentNode", doneName=done++; // combinator.first为真确保待校验元素和elem严格父子或邻近节点关系 // 返回matcher执行结果,中断循环;否则遍历元素执行matcher,匹配时跳出循环 return combinator.first ? function(elem,context,xml){ while ( (elem=elem[dir]) ){ if ( elem.nodeType===1 || checkNonElements ){ return matcher(elem,context,xml); } } } : function(elem,context,xml){ var oldCache, uniqueCache, outerCache, newCache=[dirruns,doneName]; // 只要有祖先节点和兄弟节点满足校验条件 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 ){ return (newCache[2]=oldCache[2]); }else{ uniqueCache[key]=newCache; if ( (newCache[2]=matcher(elem,context,xml)) ){ return true; } } } } } }; } // 返回校验函数,elem匹配matchers中所有校验器的验证规则,返回真,否则为否 // 选择器自右向左校验 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]; } // 多个上下文contexts中查找selector元素 function multipleContexts(selector,contexts,results){ var i=0, len=contexts.length; for ( ; i<len; i++ ){ Sizzle(selector,contexts[i],results); } return results; } // 校验函数filter过滤元素unmatched,过滤结果作为返回值,map存储匹配的序号 // 除了filter函数过滤外,unmatched需保证为真值 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; } // 以:not选择器为例,preFilter为:not之前选择器的校验函数, // selector为:not之前选择器完全值,后加"*"或"" // matcher为:not选择器的校验函数 // postFilter为:not之后的校验函数,"+"等操作符前 // postFinder为:not之后的校验函数,"+"等操作符后 // postSelector为选择器完全值 // 创建特殊校验函数并返回,用于查找:not、:first等选择器 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=[],// :not前选择器校验函数匹配结果的序号集 postMap=[],// :not选择器校验函数匹配结果的序号集 preexisting=results.length, // 无seed情况下,调用multipleContexts找到匹配:not前选择器的元素 elems=seed || multipleContexts(selector||"*",context.nodeType?[context]:context,[]), // condense(unmatched,map,filter,context,xml)校验函数filter过滤元素unmatched,过滤结果作为返回值,map存储匹配的序号 // 使用preFilter过滤elems元素,过滤后的结果集赋值给matcherIn,preMap过滤结果的序号集 matcherIn=preFilter && (seed || !selector) ? condense(elems,preMap,preFilter,context,xml) : elems, // 无matcher,matcherOut赋值为matcherIn 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; // 特殊Expr.filter方法过滤matcherIn,过滤结果写入matcherOut // matcher设置的标准,matcherIn中匹配项记为false,matcherOut带序号包含匹配的节点 if ( matcher ){ matcher(matcherIn,matcherOut,context,xml); } // postFilter也是特殊过滤器 if ( postFilter ) { temp=condense(matcherOut,postMap);// 将matcherOut拷贝给temp,postMap过滤结果的序号集 postFilter(temp,[],context,xml);// 将temp匹配项设为否值 i=temp.length; while ( i-- ){ if ( (elem=temp[i]) ){ // 将matcherOut中不匹配项设为否值 matcherOut[postMap[i]]=!(matcherIn[postMap[i]]=elem); } } } // matcherOut之前校验函数的结果,未匹配的记为false,匹配的为节点元素, // 有seed,返回结果results不匹配的元素记为否值 // 无seed,if语句清洗值为false的matcherOut数组项,results返回匹配元素 if ( seed ){ if ( postFinder || preFilter ){ if ( postFinder ){ temp=[]; i=matcherOut.length; while ( i-- ){ if ( (elem=matcherOut[i]) ){ temp.push( (matcherIn[i]=elem) );// 匹配的元素 } } // setMatcher(postFinder,postSelector)查询temp下所有元素节点并过滤 postFinder(null,(matcherOut=[]),temp,xml); } i=matcherOut.length; while ( i-- ){ if ( (elem=matcherOut[i]) && (temp=postFinder ? indexOf(seed,elem) : preMap[i])>-1 ){ // seed匹配的元素记为否值,results不匹配的元素记为否值 seed[temp]=!(results[temp]=elem); } } } }else{ // matchOut与results相同,返回空数组,否则返回matchOut matcherOut=condense( matcherOut===results ? matcherOut.splice(preexisting,matcherOut.length) : matcherOut ); if( postFinder ){ // setMatcher(postFinder,postSelector)查询temp下所有元素节点并过滤,与results合并 postFinder(null,results,matcherOut,xml); }else{ push.apply(results,matcherOut); } } }); } // 获取校验函数 // 获取校验函数,matcher[expando]为真时通过setMatcher获取特殊的校验函数,单函数形式 // 普通的校验函数为数组形式 // 逗号选择器分割生成多个groups、单个groups有多个tokens // 也即单个groups只可能返回数组形式的普通校验函数或单函数形式的特殊校验函数 function matcherFromTokens(tokens){ var checkContext, matcher, j, len=tokens.length, leadingRelative=Expr.relative[tokens[0].type], implicitRelative=leadingRelative || Expr.relative[" "], i=leadingRelative ? 1 : 0,// 起始无"+|~| |>",从第二个项开始查找 // 选择器以">"起始等情况,判断addCombinator层级关系校验器修改后elem与context是否相符 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) ); checkContext=null; return ret; }]; // form#form .input选择器,else语句分支添加form、#form两个校验函数 // 空格匹配Expr.relative[tokens[i].type],进入if语句,addCombinator将form、#form两个校验函数合并为一 // addCombinator函数由传参elem(seed或context下查找所有节点)获取待校验节点 // 生成新的校验函数并返回;elem为引用对象的形式,校验过程中被改变(用于传入嵌套的校验函数.panel form#form .input) // .input生成另一校验函数,同addCombinator构建的校验函数平级 // return返回值再用elementMatcher进行包装,先执行.input校验函数,再执行addCombinator构建的校验函数 for ( ; i<len; i++ ){ if ( (matcher=Expr.relative[tokens[i].type]) ){ matchers=[addCombinator(elementMatcher(matchers),matcher)]; }else{ // Expr.filter[tokens[i].type]传参为待匹配的选择器参数 // 通过Expr.filter[tokens[i].type]返回校验函数 // 如.className选择器,matcher为Expr.filter["ClASS"]("className") // gt(1)选择器,matcher为Expr.pesudos["gt"](0) matcher=Expr.filter[tokens[i].type].apply(null,tokens[i].matches); // matcher[expando]为真时,特殊的过滤方法,通过传递引用对象的形式改写后获取结果 // 普通过滤通过返回值得到结果 // :not、:first等伪类可能是matcher[expando]真值情况,返回过滤函数 if ( matcher[expando] ){ j=++i; for ( ; j<len; j++ ){ if ( Expr.relative[tokens[j].type] ){ break; } } return setMatcher( i>1 && elementMatcher(matchers),// :not之前选择器的校验函数 i>1 && toSelector( tokens.slice(0,i-1).concat({value:tokens[i-2].type===" " ? "*" : "" }) ).replace(rtrim,"$1"),// :not之前选择器完全值,后加"*"或"" matcher,// :not选择器的校验函数 i<j && matcherFromTokens(tokens.slice(i,j)),// :not之后的校验函数,"+"等操作符前 j<len && matcherFromTokens((tokens=tokens.slice(j))),// :not之后的校验函数,"+"等操作符后 j<len && toSelector(tokens)// 选择器完全值 ); } matchers.push(matcher); } } return elementMatcher(matchers); } // 由校验函数集elementMatchers、setMatchers构建superMatch超级匹配函数并返回 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, // 无待匹配元素seed,outermost上下文中获取所有元素节点 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; } for ( ; i!==len && (elem=elems[i])!=null; i++ ){ // 在outermost中找出所有tag节点,调用elementMatchers中匹配函数校验其是否满足条件 // 逗号分割的选择器前后无:first等需要特殊校验的选择器,results直接推入匹配的元素 if ( byElement && elem ){ j=0; if ( !context && elem.ownerDocument!==document ){ setDocument(elem); xml=!documentIsHTML; } // 怎样处理addCombinator层级校验函数中elem被改变的事实??? while ( (matcher=elementMatchers[j++]) ){ if ( matcher(elem,context || document,xml) ){ results.push(elem); break; } } if ( outermost ){ dirruns=dirrunsUnique; } } if ( bySet ){ // matchedCount标记elem元素不匹配elementMatchers普通校验,需要用setMatchers校验 if ( (elem=!matcher && elem) ){ matchedCount--; } // 无seed时,setMatchers将在context查找所有节点并寻找匹配元素 // 有seed,setMatchers将在context下查找unmatched内元素 if ( seed ){ unmatched.push(elem); } } } // 已匹配elementMatchers的元素个数,其余元素需要用setMatchers校验 matchedCount+=i; if ( bySet && i!==matchedCount ){ j=0; while ( (matcher=setMatchers[j++]) ){ // 包装后的setMatchers获得unmatched匹配的为否值,setMatched只包含匹配的元素 // 无seed时,unmatched为空数组,context查找所有节点并寻找匹配元素 // 有seed,context下查找unmatched内元素 matcher(unmatched,setMatched,context,xml); } if ( seed ){ // 有seed时,setMatchers过滤结果中setMatched未匹配项记为否 if ( matchedCount>0 ){ while ( i-- ){ if ( !(unmatched[i] || setMatched[i]) ){ setMatched[i]=pop.call(results); } } } // 移除值为false的数组项,保留值为节点的数组项,即匹配结果 setMatched=condense(setMatched); } // select方法将直接获得results结果集,无须返回results push.apply(results,setMatched); 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; } // 获取超级匹配函数 // 通过matcherFromTokens获取校验函数集elementMatchers、setMatchers // 接着由matcherFromGroupMatchers函数构建superMatch函数并返回 // 返回值为函数,该函数的返回值为superMatch超级查询并校验匹配函数 compile=Sizzle.compile=function(selector,match/* Internal Use Only */){ var i, setMatchers=[], elementMatchers=[], cached=compilerCache[selector+" "]; if ( !cached ){ if ( !match ){ match=tokenize(selector); } i=match.length;// 逗号分隔的选择器个数 while ( i-- ){ // 逗号分隔的选择器通过matcherFromTokens获取校验函数 // 只可能是数组形式的普通检验函数,推送到elementMatchers中 // 或者函数形式的特殊校验函数,推送到setMatchers中 // 因此setMatchers、elementMatchers各自获取到的结果是逗号分割选择器的各自结果,不需要再过滤 cached=matcherFromTokens(match[i]); if ( cached[expando] ){ // 特殊校验 如:not、:first等,以待校验元素、结果集作为参数传入过滤 // 引用对象形式过滤待校验元素及结果集 setMatchers.push(cached); }else{ // 单元素校验 如tag、id等,以校验元素作为参数传入,返回值为布尔类型 elementMatchers.push(cached); } } // 返回值,向superMatch函数传入校验函数集elementMatchers、setMatchers // cached存储校验函数 cached=compilerCache(selector,matcherFromGroupMatchers(elementMatchers,setMatchers)); cached.selector=selector; } return cached; }; // 在不能使用qsa或兼容性问题,或选择器语句复杂的情况下,使用select函数进行查询 // 单一选择器无逗号分割时,调用getElementById或Expr.find[type]进行查找 // 有逗号分割,调用select方法遍历dom树,Expr.filter方法过滤,获取结果集 select=Sizzle.select=function(selector,context,results,seed){ var i, tokens, token, type, find, compiled=typeof selector==="function" && selector,// 选择器为超级匹配函数 match=!seed && tokenize( (selector=compiled.selector || selector) ); results=results || []; // 逗号分隔的选择器只有一个,且不存在seed if ( match.length===1 ){ // id选择器快速查找," #id + :lt(3) > .tag"快速找到#id,并且更新context tokens=match[0]=match[0].slice(0); if ( tokens.length>2 && (token=tokens[0]).type==="ID" && support.getById && 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; }else if( compiled ){// selector为函数 context=context.parentNode; } selector=selector.slice(tokens.shift().value.length);// 移除已经找到的id节点 } i=matchExpr["needsContext"].test(selector) ? 0 : tokens.length; // 其他选择器或以"+|~| |>"的选择器通过Expr.find[type]查找 while ( i-- ){// 选择器自右向左查找 token=tokens[i]; // "+","~",">"," "执行跳出循环 if ( Expr.relative[(type=token.type)] ){ break; } // "+ :lt(3) > .tag"情形找到.tag,其余通过超级匹配函数superMatch查找 if ( (find=Expr.find[type]) ){ // 选择器以">"、" "起始context中查询,选择器以"+"、"~"起始context父节点中查询 if ( (seed=find( token.matches[0].replace(runescape,funescape), rsibling.test(tokens[0].type) && testContext(context.parentNode) || context )) ){ tokens.splice(i,1); selector=seed.length && toSelector(tokens); if ( !selector ){// seed存储已找到的元素,选择器查询完毕时,填入results中 push.apply(results,seed); return results; } break; } } } } // 传参有seed,或Expr.find无法找到的内容,调用超级匹配函数superMatch查询文档树,并获得结果集 ( compiled || compile(selector,match) )( seed,// 待匹配元素 context,// 上下文 !documentIsHTML, results,// 查询结果集 // superMatch函数查找所有tag节点的上下文 !context || rsibling.test(selector) && testContext(context.parentNode) || context ); return results; }; // 將hasDuplicate赋值为true,support.sortStable为真 support.sortStable=expando.split("").sort(sortOrder).join("")===expando; // Support: Chrome 14-35+ support.detectDuplicates设定为真值,允许重复项 support.detectDuplicates=!!hasDuplicate; // 初始化设置document,兼容性情况support获取,根据兼容性设置部分特殊的find、filter方法 setDocument(); // Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) // 两个未插入文档的元素调用compareDocumentPosition方法理应返回1,不兼容时返回4 // support.sortDetached为否 support.sortDetached=assert(function(el){ return el.compareDocumentPosition(document.createElement("fieldset")) & 1; }); // 针对兼容性问题添加特殊的属性获取方法 // Support: IE<8 if ( !assert(function(el){ el.innerHTML="<a href='#'></a>"; return el.firstChild.getAttribute("href")==="#"; }) ){// 没报错向Expr.attrHandle添加方法 addHandle("type|href|height|width",function(elem,name,isXML){ if ( !isXML ){ // 次参为2时,属性值以字符串形式返回;为1时,严格区分大小写 // https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx return elem.getAttribute(name,name.toLowerCase()==="type" ? 1 : 2 ); } }); } // Support: IE<9 使用elem.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,isXM ){ if ( !isXML && elem.nodeName.toLowerCase()==="input" ){ return elem.defaultValue; } }); } // Support: IE<9 使用getAttributeNode方法获取布尔值,替代取值有问题的getAttribute 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; } }); } var _sizzle=window.Sizzle; // window.Sizzle被占用的情况下,使用完毕执行noConflict,window.Sizzle还原为初始值 Sizzle.noConflict=function() { if ( window.Sizzle===Sizzle ) { window.Sizzle=_sizzle; } return Sizzle; }; // AMD/CMD模块化加载 if ( typeof define==="function" && define.amd ){ define(function(){ return Sizzle; }); }else if( typeof module!=="undefined" && module.exports ){ module.exports=Sizzle; }else{ window.Sizzle=Sizzle; } })( window );
附
selector-sizzle.js
define([ "./core", "../external/sizzle/dist/sizzle" ],function(jQuery,Sizzle){ jQuery.find=Sizzle;// 查找,第四参数为期望匹配项,若有,从期望匹配项中查找结果 jQuery.expr=Sizzle.selectors;// 开发者扩展sizzle jQuery.expr[":"]=jQuery.expr.pseudos;// 添加伪类选择器 jQuery.uniqueSort=jQuery.unique=Sizzle.uniqueSort;// 去重并排序 jQuery.text=Sizzle.getText;// 拼接文本 jQuery.isXMLDoc=Sizzle.isXML;// 是否xml jQuery.contains=Sizzle.contains;// 包含 jQuery.escapeSelector=Sizzle.escape;// 转义 });
selector-native.js(不使用sizzle,而用qsa原生语句)
define([ "./core", "./var/document", "./var/documentElement", "./var/hasOwn", "./var/indexOf" ],function(jQuery,document,documentElement,hasOwn,indexOf){ // 不使用sizzle,使用qsa查询 var hasDuplicate, sortInput, sortStable=jQuery.expando.split("").sort(sortOrder).join("")===jQuery.expando, matches=documentElement.matches || documentElement.webkitMatchesSelector || documentElement.mozMatchesSelector || documentElement.oMatchesSelector || documentElement.msMatchesSelector, rcssescape=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g, fcssescape=function(ch,asCodePoint){ if ( asCodePoint ){ if ( ch==="\0" ){ return "\uFFFD"; } return ch.slice(0,-1)+"\\"+ch.charCodeAt(ch.length-1).toString(16)+" "; } return "\\"+ch; }; function sortOrder(a,b){ if ( a===b ) { hasDuplicate=true; return 0; } var compare=!a.compareDocumentPosition-!b.compareDocumentPosition; if ( compare ){ return compare; } compare=( a.ownerDocument || a )===( b.ownerDocument || b ) ? a.compareDocumentPosition(b) : 1; if ( compare & 1 ){ if ( a===document || a.ownerDocument===document && jQuery.contains(document,a) ){ return -1; } if ( b===document || b.ownerDocument===document && jQuery.contains(document,b) ){ return 1; } return sortInput ? ( indexOf.call(sortInput,a)-indexOf.call(sortInput,b) ) : 0; } return compare & 4 ? -1 : 1; } // 调用sortOrder先排序,再去重 function uniqueSort(results){ var elem, duplicates=[], j=0, i=0; hasDuplicate=false;// 有无重复项 sortInput=!sortStable && results.slice(0); results.sort(sortOrder); if ( hasDuplicate ){ while ( ( elem=results[i++ ]) ){ if ( elem===results[i] ){ j=duplicates.push(i); } } while ( j-- ){ results.splice(duplicates[j],1); } } sortInput=null; return results; } function escape(sel){ return (sel+"").replace(rcssescape,fcssescape); } jQuery.extend( { uniqueSort:uniqueSort,// 去重并排序 unique:uniqueSort, escapeSelector:escape,// 转义 // 有seed,h5语句过滤查询,无seed,qsa语句查询 find: function(selector,context,results,seed){ var elem, nodeType, i = 0; results=results || []; context=context || document; if ( !selector || typeof selector!=="string" ){ return results; } if ( ( nodeType=context.nodeType )!==1 && nodeType!==9 ){ return []; } if ( seed ){ while ( ( elem=seed[i++] ) ) { if ( jQuery.find.matchesSelector(elem,selector) ){ results.push(elem); } } } else { jQuery.merge(results,context.querySelectorAll(selector)); } return results; }, // 以textContent或nodeValue属性获取单元素或多元素的文本内容 text:function(elem){ var node, ret="", i=0, nodeType=elem.nodeType; if ( !nodeType ){ while ( ( node=elem[i++] ) ){ ret += jQuery.text(node); } }else if( nodeType===1 || nodeType===9 || nodeType===11 ){ return elem.textContent; }else if( nodeType===3 || nodeType===4 ){ return elem.nodeValue; } return ret; }, // 原生语句判断是否包含,a、b为dom对象的形式 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(bup) ); }, // 判断是否xml isXMLDoc:function(elem){ var documentElement=elem && (elem.ownerDocument || elem).documentElement; return documentElement ? documentElement.nodeName!=="HTML" : false; }, expr:{ attrHandle:{},// 由开发者添加获取属性的方法 match: { bool:new RegExp( "^(?:checked|selected|async|autofocus|autoplay|controls|defer" + "|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped)$", "i" ), needsContext: /^[\x20\t\r\n\f]*[>+~]/ } } } ); jQuery.extend(jQuery.find,{ // elements滤除不匹配expr的元素 matches:function(expr,elements){ return jQuery.find(expr,null,null,elements); }, // 使用原生语句判断元素是否匹配选择器 matchesSelector:function(elem,expr){ return matches.call(elem,expr); }, // 自定义jQuery.expr获取属性,区别对待xml的考虑,否则使用elem.getAttribute方法 attr:function(elem,name){ var fn=jQuery.expr.attrHandle[name.toLowerCase()], value=fn && hasOwn.call( jQuery.expr.attrHandle,name.toLowerCase() ) ? fn(elem,name,jQuery.isXMLDoc(elem)) : undefined; return value!==undefined ? value : elem.getAttribute(name); } }); });
相关推荐
要深入理解jQuery表单选择器的源码,可以阅读`jQuery源码`和`Sizzle源码`,同时结合实际项目进行实践。`第7章 表单选择器.pdf`文档可能是对这部分内容的一个详细讲解,建议配合源码阅读,加深理解。 总结,jQuery...
Sizzle源码中的优化策略,如缓存已匹配的元素、避免不必要的DOM遍历,都值得我们深入研究。 8. **自定义选择器** jQuery还允许用户扩展自定义选择器,这在源码中体现为注册新的选择器函数。理解这一机制,可以帮助...
在阅读和理解Sizzle源码时,关键在于跟踪其如何根据不同的选择器类型和浏览器能力来决定使用哪一种获取DOM元素的方法。同时,源码中的详细注释能够帮助开发者更好地理解每个部分的作用和执行逻辑,是研究jQuery源码...
在分析Sizzle源码之前,先整理一下选择器的工作原理 先明确一些选择器中用到的名词,后边阅读时不会有歧义: 选择器表达式: “div > p” 块表达式: “div” “p” 并列选择器表达式: “div, p” 块分割器: ...
通过查看源码,开发者可以学习到更多关于Sizzle实现的细节,比如如何处理复杂的CSS选择器组合,以及如何与jQuery其他组件协同工作。 总的来说,Sizzle引擎是jQuery的核心组成部分,它的强大功能和跨浏览器兼容性...
Sizzle的源码是一个很好的学习资源,可以深入了解CSS选择器的实现细节,以及如何在JavaScript中高效地处理DOM。通过阅读和分析Sizzle的代码,开发者可以学习到如何编写高效、跨浏览器的JavaScript代码,这对于任何...
在Sizzle源码的405行开始,有一个名为`Expr`的对象,其中定义了一系列的过滤类型,包括"ID"、"TAG"、"CLASS"、"ATTR"、"CHILD"、"PSEUDO"。这些过滤类型对应着CSS选择器的不同部分,`tokenize`函数生成的标记将会...
1. **源码文件**:通常包括JavaScript文件,如`sizzle.js`,这是Sizzle的核心实现。 2. **测试用例**:为了确保Sizzle的正确性和性能,压缩包可能包含测试文件,通过这些测试用例,开发者可以了解Sizzle如何处理各种...
《jQuery源码分析》 jQuery,作为一款广泛使用的JavaScript库,极大地简化了DOM操作、事件处理、动画制作以及Ajax交互。深入理解其源码对于提升JavaScript编程技能和优化前端性能至关重要。本文将从核心概念、设计...
《jQuery源码深度解析》 jQuery,作为一款广泛使用的JavaScript库,因其简洁的API和强大的功能,深受开发者喜爱。本文将深入探讨jQuery 1.2.6版本的源码,结合其中的中文注释,帮助读者理解其内部机制,提升...
在深入学习Sizzle时,了解其源码也是非常有益的。你可以查看Sizzle的实现细节,例如它是如何处理选择器的分组、如何优化性能,以及如何处理不同的浏览器兼容性问题。此外,通过对比不同版本的jQuery,你可以发现...
通过研究Sizzle源码,我们可以了解如何高效地查找和操作DOM元素。 4. **事件处理**:jQuery的事件处理机制封装了浏览器之间的差异,提供了统一的API。它使用`addEventListener`和`attachEvent`(针对IE)进行事件...
无论你是初学者,希望通过阅读源码了解基础的DOM操作和事件处理,还是经验丰富的开发者,想要深入理解其内部机制,如选择器引擎Sizzle、动画效果实现等,都能从中找到启示。 标签“jQuery”揭示了本文的主题。...
《深入解析jQuery 1.11源码》 jQuery,作为JavaScript库的杰出代表,以其简洁易用的API,极大地简化了DOM操作、事件处理、动画制作和Ajax交互。jQuery 1.11版本是其1.x系列的一个稳定版本,为开发者提供了强大的...
Sizzle 是一套前端用智能来处理模块和组件依赖关系的框架。 它遵循 CommonJS 的运行规范来完成对各个模块的动态加载。而且可以同时包含其他库而不产生冲突。 Sizzle的使用很简单,其下共有 5 个常用的方法, 只...
《jQuery源码详细分析中文注释》是一份深入解析jQuery库源码的宝贵资源,它为开发者提供了理解这个广泛使用的JavaScript库内部工作机制的机会。jQuery以其简洁的API和强大的功能深受前端开发者的喜爱,但其背后的...
《jQuery源码解析》 jQuery,作为一款广泛应用于前端开发的JavaScript库,因其简洁的API和强大的功能,深受开发者喜爱。本文将深入探讨jQuery 1.12版本的源码,帮助读者理解其核心机制,提升JavaScript技术能力。 ...
通过阅读源码,我们可以学习到如何编写高效的选择器,以及如何利用Sizzle实现复杂的DOM遍历。 3. **DOM操作** jQuery提供了丰富的DOM操作方法,如`$(selector).html()`, `append()`, `prepend()`等。在源码中,...
《jQuery源码注释解析》 jQuery,作为一款广受欢迎的JavaScript库,极大地简化了DOM操作、事件处理、动画制作以及Ajax交互等任务。它的源码注释是深入理解其内部工作原理的重要途径,这对于开发者来说是提升技能、...