首先使用原生语句查找,其次在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
、+、~起始,*匹配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-
附
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); } }); });