首先使用原生语句查找,其次在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 />或<div />
"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 elem| 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);
}
});
});