简介
Sizzle 的静态方法起了辅助的作用,帮助完成一些对元素属性获取或者转义一些选择器,完成一些对元素节点的排序等等。我会对这些方法进行一个注释与讲解,等把所有的这些辅助方法讲完后,会对sizzle的是怎么样选择到元素的一个流程,还有架构做一个梳理。解读方法的顺序基本是按照代码的先后顺序来讲。如果有误,请大家纠正。
matches
对已有元素进行筛选
/**
* [matches] 对已有元素进行筛选
* @param {[type]} expr
* @param {[type]} elements
* @return Sizzle elements
*/
Sizzle.matches = function(expr, elements) {
//selector, context, results, seed,这个就是种子元素中来过滤查找元素,不用在上下文中重新来查找
return Sizzle(expr, null, null, elements);
};
Sizzle.matchesSelector
查看当前元素含有的是否包含这个选择器
/**
* [matchesSelector ] 查看当前元素含有的是否包含这个选择
* @param {[element]} elem
* @param {[string]} expr
* @return {[boolean]} true or false
*/
Sizzle.matchesSelector = function(elem, expr) {
// 设置当前的上下文的兼容性
setDocument(elem);
/**
* support.matchesSelector 是否支持
* documentIsHTML 是不是html
* !nonnativeSelectorCache[expr + " "] 查看是否在缓存中为false
* rbuggyMatches 不存在或!rbuggyMatches.test(expr) matches不存在bug
* rbuggyQSA不存在或 就是QSA不存在bug
*/
if (support.matchesSelector && documentIsHTML &&
!nonnativeSelectorCache[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) {
// 直接返回true
return ret;
}
} catch (e) {
// 如果不能支持选择器放入 nonnativeSelectorCache
nonnativeSelectorCache(expr, true);
}
}
// 走入catch就会走这里,重新进行元素的一个匹配,如果找道返回true
return Sizzle(expr, document, null, [elem]).length > 0;
};
Sizzle.contains
来表示传入的节点是否为该节点的后代节点,返回true or false
/**
* [contains]
* @param {[element]} context
* @param {[element]} elem
* @return {[boolean]}
*/
Sizzle.contains = function(context, elem) {
// Set document vars if needed
// Support: IE 11+, Edge 17 - 18+
// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
// two documents; shallow comparisons work.
// eslint-disable-next-line eqeqeq
// 根据文档来设置上下问
if ((context.ownerDocument || context) != document) {
setDocument(context);
}
// 来表示传入的节点是否为该节点的后代节点,返回true or false
return contains(context, elem);
};
Sizzle.escape
转义css选择器,首先把选择器转为字符串,然后用正则匹配需要转义的字符串,替换成unicode字符。这两个方法我已经在前置内容中已经讲过了。大家没看的可以从前几篇文章中找一下呢
Sizzle.escape = function(sel) {
return (sel + "").replace(rcssescape, fcssescape);
};
Sizzle.error
传参返回一个错误提示
Sizzle.error = function(msg) {
throw new Error("Syntax error, unrecognized expression: " + msg);
};
Sizzle.uniqueSort
这个方法主要做的是给文档排序,上次节代码里面我已经讲到了怎么给文档排序了,不了的可以查看一下。还有一个做用就是删除重复的文档。我记得上节也讲到了sortOrder 这个方法了,如果a === b 相等时候 hasDuplicate 为true,说明文档有重复或者元素重复。
// Support: Chrome 14-35+ // Always assume duplicates if they aren't passed to the comparison function 加入没有传递这个参数就是一只重复着 support.detectDuplicates = !!hasDuplicate; 其实就是转了个boolean类型。这行代码在jquery的2921行。
// One-time assignments
// Sort stability 这个代码是对排序稳定性的一个测试, expando 是sizzle + 一个时间戳。 sortOrder = function( a, b ) { if ( a === b ) { hasDuplicate = true; } return 0; } // 就这块return 0 expando还是原来的顺序 hasDuplicate = true; support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando;
/**
* Document sorting and removing duplicates
* @param {ArrayLike} results
*/
Sizzle.uniqueSort = function(results) {
var elem,
duplicates = [],
j = 0,
i = 0;
// Unless we *know* we can detect duplicates, assume their presence
// Document的时候回执行sortOrder方法,如果支持hasCompare? hasDuplicate是true,否则是false
hasDuplicate = !support.detectDuplicates;
//sizzle1622222893304 就是false,&& results.slice(0) 把result 返回一个新数组浅拷贝
sortInput = !support.sortStable && results.slice(0);
然后对results进行排序
results.sort(sortOrder);
if (hasDuplicate) {
while ((elem = results[i++])) {
if (elem === results[i]) {
j = duplicates.push(i);
}
}
// j 等于duplicates.length 存储的就是数字,duplicates存储的是重复的数字的位置
while (j--) {
results.splice(duplicates[j], 1);
}
}
// Clear input after sorting to release objects
// See https://github.com/jquery/sizzle/pull/225
sortInput = null;
返回删剩下没有重复的元素
return results;
};
Sizzle.getText
获取元素或者一组元素的文本内容
/**
* Utility function for retrieving the text value of an array of DOM nodes
* @param {Array|Element} elem
*/
、、
getText = Sizzle.getText = function( elem ) {
var node,
ret = "",
i = 0,
nodeType = elem.nodeType;
// 如果不存在nodeType属性,说明是类数组
if ( !nodeType ) {
// If no nodeType, this is expected to be an array
while ( ( node = elem[ i++ ] ) ) {
// 递归拿到元素的内容
// Do not traverse comment nodes
ret += getText( node );
}
// 如果是元素或者document 或者 文档片段
} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
// Use textContent for elements
// innerText usage removed for consistency of new lines (jQuery #11153)
// 返回元素中文本内容
if ( typeof elem.textContent === "string" ) {
return elem.textContent;
} else {
否则遍历递归每个兄弟节点拿到文本
// Traverse its children
for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
ret += getText( elem );
}
}
// 若果是CDATA节点或者文本节点 ,返回nodevalue
} else if ( nodeType === 3 || nodeType === 4 ) {
return elem.nodeValue;
}
// Do not include comment or processing instruction nodes
return ret;
};
Sizzle.selectors
这个方法主要是绑定了 cacheLength、createPseudo、match、attrHandle、find、preFilter、filter、pseudos这个几个方法。
cacheLength 这个属性的作用是 方法createCache 的中keys的长度,如果超过keys的长度超过了50,让第一个缓存出队。
createPseudo:markFunction 这个方法默认是接收一个fn参数,给fn添加一个[expando] = true的属性。并返回这个fn。
matchExpr这个属性这个在分析一之时,我分析过了所以的属性,用来匹配各种选择器。 attrHandle: 空对象 Expr.attrHandle[arr[i]] = handler;
find: 空对象,在setDocument初始化的的时候赋予的对应的key:value,返回元素数组 不明白看看上一章
relative: 选择的相对位置
preFilter: 预过滤器
filter:过滤器 在setDocument初始化的的时候赋予的对应的key:value,返回true or false
pseudos:伪类选择器
这块处理我会在下节继续接着解析css选择器,选中html元素结合着把这块来一个传插,并举一些实际的代码。 这块相对比较好理解,会举几个方法来讲一下。这块不理解可以先看,下章sizzle是怎么选择对这些选择器是如何处理的。
Sizzle.selectors = {
// Can be adjusted by the user
cacheLength: 50,
createPseudo: markFunction,
match: matchExpr,
attrHandle: {},
find: {},
//我们匹配选择器dir 这样处理 relative['dir'] ,通过这样来拿到这元素位置
relative: {
">": { dir: "parentNode", first: true },
" ": { dir: "parentNode" },
"+": { dir: "previousSibling", first: true },
"~": { dir: "previousSibling" }
},
// 这块的preFilter做的处理,预先解析,生成的token做一个预判,
preFilter: {
"ATTR": function( match ) {
match[ 1 ] = match[ 1 ].replace( runescape, funescape );
// Move the given value to match[3] whether quoted or unquoted
match[ 3 ] = ( match[ 3 ] || match[ 4 ] ||
match[ 5 ] || "" ).replace( runescape, funescape );
if ( match[ 2 ] === "~=" ) {
match[ 3 ] = " " + match[ 3 ] + " ";
}
return match.slice( 0, 4 );
},
/*
match: [0: ":nth-child(2)"
1: "nth"
2: "child"
3: "2"
4: 0
5: 2
6: undefined
7: ""
8: "2"
groups: undefined
index: 0
input: ":nth-child(2)"
length: 9]
*/
"CHILD": function( match ) {
/* matches from matchExpr["CHILD"]
1 type (only|nth|...)
2 what (child|of-type)
3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
4 xn-component of xn+y argument ([+-]?\d*n|)
5 sign of xn-component
6 x of xn-component
7 sign of y-component
8 y of y-component
*/
// 拿到nth后代选择器
match[ 1 ] = match[ 1 ].toLowerCase();
// 如果是nth
if ( match[ 1 ].slice( 0, 3 ) === "nth" ) {
// 判断后端选择的nth-child(arg) arg是否有参数or 0
// nth-* requires argument
if ( !match[ 3 ] ) {
Sizzle.error( match[ 0 ] );
}
// numeric x and y parameters for Expr.filter.CHILD
// remember that false/true cast respectively to 0/1
match[ 4 ] = +( match[ 4 ] ?
match[ 5 ] + ( match[ 6 ] || 1 ) :
2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" ) );
match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" );
其他选择器报错
// other types prohibit arguments
} else if ( match[ 3 ] ) {
Sizzle.error( match[ 0 ] );
}
//返回组装后的租户在哪个结果
return match;
},
"PSEUDO": function( match ) {
var excess,
unquoted = !match[ 6 ] && match[ 2 ];
if ( matchExpr[ "CHILD" ].test( match[ 0 ] ) ) {
return null;
}
// Accept quoted arguments as-is
if ( match[ 3 ] ) {
match[ 2 ] = match[ 4 ] || match[ 5 ] || "";
// Strip excess characters from unquoted arguments
} else if ( unquoted && rpseudo.test( unquoted ) &&
// Get excess from tokenize (recursively)
( excess = tokenize( unquoted, true ) ) &&
// advance to the next closing parenthesis
( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) {
// excess is a negative index
match[ 0 ] = match[ 0 ].slice( 0, excess );
match[ 2 ] = unquoted.slice( 0, excess );
}
// Return only captures needed by the pseudo filter method (type and argument)
return match.slice( 0, 3 );
}
},
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 ) {
return function( elem ) {
var result = Sizzle.attr( elem, name );
if ( result == null ) {
return operator === "!=";
}
if ( !operator ) {
return true;
}
result += "";
/* eslint-disable max-len */
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;
/* eslint-enable max-len */
};
},
"CHILD": function( type, what, _argument, first, last ) {
var simple = type.slice( 0, 3 ) !== "nth",
forward = type.slice( -4 ) !== "last",
ofType = what === "of-type";
return first === 1 && last === 0 ?
// Shortcut for :nth-*(n)
function( elem ) {
return !!elem.parentNode;
} :
function( elem, _context, xml ) {
var cache, uniqueCache, outerCache, node, nodeIndex, start,
dir = simple !== forward ? "nextSibling" : "previousSibling",
parent = elem.parentNode,
name = ofType && elem.nodeName.toLowerCase(),
useCache = !xml && !ofType,
diff = false;
if ( parent ) {
// :(first|last|only)-(child|of-type)
if ( simple ) {
while ( dir ) {
node = elem;
while ( ( node = node[ dir ] ) ) {
if ( ofType ?
node.nodeName.toLowerCase() === name :
node.nodeType === 1 ) {
return false;
}
}
// Reverse direction for :only-* (if we haven't yet done so)
start = dir = type === "only" && !start && "nextSibling";
}
return true;
}
start = [ forward ? parent.firstChild : parent.lastChild ];
// non-xml :nth-child(...) stores cache data on `parent`
if ( forward && useCache ) {
// Seek `elem` from a previously-cached index
// ...in a gzip-friendly way
node = parent;
outerCache = node[ expando ] || ( node[ expando ] = {} );
// Support: IE <9 only
// Defend against cloned attroperties (jQuery gh-1709)
uniqueCache = outerCache[ node.uniqueID ] ||
( outerCache[ node.uniqueID ] = {} );
cache = uniqueCache[ type ] || [];
nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
diff = nodeIndex && cache[ 2 ];
node = nodeIndex && parent.childNodes[ nodeIndex ];
while ( ( node = ++nodeIndex && node && node[ dir ] ||
// Fallback to seeking `elem` from the start
( diff = nodeIndex = 0 ) || start.pop() ) ) {
// When found, cache indexes on `parent` and break
if ( node.nodeType === 1 && ++diff && node === elem ) {
uniqueCache[ type ] = [ dirruns, nodeIndex, diff ];
break;
}
}
} else {
// Use previously-cached element index if available
if ( useCache ) {
// ...in a gzip-friendly way
node = elem;
outerCache = node[ expando ] || ( node[ expando ] = {} );
// Support: IE <9 only
// Defend against cloned attroperties (jQuery gh-1709)
uniqueCache = outerCache[ node.uniqueID ] ||
( outerCache[ node.uniqueID ] = {} );
cache = uniqueCache[ type ] || [];
nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
diff = nodeIndex;
}
// xml :nth-child(...)
// or :nth-last-child(...) or :nth(-last)?-of-type(...)
if ( diff === false ) {
// Use the same loop as above to seek `elem` from the start
while ( ( node = ++nodeIndex && node && node[ dir ] ||
( diff = nodeIndex = 0 ) || start.pop() ) ) {
if ( ( ofType ?
node.nodeName.toLowerCase() === name :
node.nodeType === 1 ) &&
++diff ) {
// Cache the index of each encountered element
if ( useCache ) {
outerCache = node[ expando ] ||
( node[ expando ] = {} );
// Support: IE <9 only
// Defend against cloned attroperties (jQuery gh-1709)
uniqueCache = outerCache[ node.uniqueID ] ||
( outerCache[ node.uniqueID ] = {} );
uniqueCache[ type ] = [ dirruns, diff ];
}
if ( node === elem ) {
break;
}
}
}
}
}
// Incorporate the offset, then check against cycle size
diff -= last;
return diff === first || ( diff % first === 0 && diff / first >= 0 );
}
};
},
"PSEUDO": function( pseudo, argument ) {
// pseudo-class names are case-insensitive
// http://www.w3.org/TR/selectors/#pseudo-classes
// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
// Remember that setFilters inherits from pseudos
var args,
fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
Sizzle.error( "unsupported pseudo: " + pseudo );
// The user may use createPseudo to indicate that
// arguments are needed to create the filter function
// just as Sizzle does
if ( fn[ expando ] ) {
return fn( argument );
}
// But maintain support for old signatures
if ( fn.length > 1 ) {
args = [ pseudo, pseudo, "", argument ];
return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
markFunction( function( seed, matches ) {
var idx,
matched = fn( seed, argument ),
i = matched.length;
while ( i-- ) {
idx = indexOf( seed, matched[ i ] );
seed[ idx ] = !( matches[ idx ] = matched[ i ] );
}
} ) :
function( elem ) {
return fn( elem, 0, args );
};
}
return fn;
}
},
pseudos: {
// Potentially complex pseudos
"not": markFunction( function( selector ) {
// Trim the selector passed to compile
// to avoid treating leading and trailing
// spaces as combinators
var input = [],
results = [],
matcher = compile( selector.replace( rtrim, "$1" ) );
return matcher[ expando ] ?
markFunction( function( seed, matches, _context, xml ) {
var elem,
unmatched = matcher( seed, null, xml, [] ),
i = seed.length;
// Match elements unmatched by `matcher`
while ( i-- ) {
if ( ( elem = unmatched[ i ] ) ) {
seed[ i ] = !( matches[ i ] = elem );
}
}
} ) :
function( elem, _context, xml ) {
input[ 0 ] = elem;
matcher( input, null, xml, results );
// Don't keep the element (issue #299)
input[ 0 ] = null;
return !results.pop();
};
} ),
"has": 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 || getText( elem ) ).indexOf( text ) > -1;
};
} ),
// "Whether an element is represented by a :lang() selector
// is based solely on the element's language value
// being equal to the identifier C,
// or beginning with the identifier C immediately followed by "-".
// The matching of C against the element's language value is performed case-insensitively.
// The identifier C does not have to be a valid language name."
// http://www.w3.org/TR/selectors/#lang-pseudo
"lang": markFunction( function( lang ) {
// lang value must be a valid identifier
if ( !ridentifier.test( lang || "" ) ) {
Sizzle.error( "unsupported lang: " + lang );
}
lang = lang.replace( runescape, funescape ).toLowerCase();
return function( elem ) {
var elemLang;
do {
if ( ( elemLang = documentIsHTML ?
elem.lang :
elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) ) ) {
elemLang = elemLang.toLowerCase();
return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
}
} while ( ( elem = elem.parentNode ) && elem.nodeType === 1 );
return false;
};
} ),
// Miscellaneous
"target": function( elem ) {
var hash = window.location && window.location.hash;
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 );
},
// Boolean properties
"enabled": createDisabledPseudo( false ),
"disabled": createDisabledPseudo( true ),
"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 );
},
"selected": function( elem ) {
// Accessing this property makes selected-by-default
// options in Safari work properly
if ( elem.parentNode ) {
// eslint-disable-next-line no-unused-expressions
elem.parentNode.selectedIndex;
}
return elem.selected === true;
},
// Contents
"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 );
},
// Element/input types
"header": function( elem ) {
return rheader.test( elem.nodeName );
},
"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" );
},
// Position-in-collection
"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 ];
} ),
"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": createPositionalPseudo( function( matchIndexes, length, argument ) {
var i = argument < 0 ?
argument + length :
argument > length ?
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;
} )
}
};
Sizzle源码分析(一) 基本概念
Sizzle源码分析(二) 工具方法
Sizzle源码分析(三) 兼容处理
Sizzle源码分析(四) sizzle静态方法分析 Sizzle源码分析(四) sizzle静态方法分析
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!