首先使用原生語句查找,其次在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);
}
});
});