上一节我们处理分组选择器,这次我们看看剩余的2种处理情况。
关系处理器处理
在层级关系中有几种特殊的划分 Token : >, +, 空格, ~ 用来表明:父与子,兄弟,祖辈子孙之间的层级关系。
selector = 'div.aaron,div > p'
从 > 划分
rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" )
可以是>+~或者空白
这个分组是为了之后的关系选择确定。
if ( (match = rcombinators.exec( soFar )) ) {
matched = match.shift();
tokens.push({
value: matched,
// Cast descendant combinators to space
type: match[0].replace( rtrim, " " )
});
soFar = soFar.slice( matched.length );
}
元素的匹配器:
Expr.filter :TAG, ID, CLASS, ATTR, CHILD, PSEUDO
通过一系列的正则抽出表达式中的内容。
ID:
///^#((?:\\.|[\w-] | [^\x00-\xa0] ) +)/
var characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+";
var ID = new RegExp("^#(" + characterEncoding + ")")
TAG:
var TAG = new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" );
CLASS:
var Class = new RegExp( "^\\.(" + characterEncoding + ")" );
ATTR:
属性选择器有点复杂,通过第一次正则只能匹配器出整体,所以需要第二次分解,引入了Expr.preFilter,Expr.preFilter保留了3个兼容处理分别是ATTR,CHILD,PSEUDO复杂的选择器。
var identifier = characterEncoding.replace( "w", "w#" );
var attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace +
// Operator (capture 2)
"*([*^$|!~]?=)" + whitespace +
// "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace +
"*\\]";
var ATTR = new RegExp( "^" + attributes );
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 );
}
<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <script src="http://code.jquery.com/jquery-latest.js"></script> <title></title> </head> <body> <button>sizzle的词法分析</button> <div id="text"> <div class="aaron"> <input type="checkbox" name="readme" /> <input type="checkbox" name="ttt" /> <input type="checkbox" name="aaa" /> <p>慕课网</p> </div> </div> <div> <input type="checkbox" name="readme" /> <input type="checkbox" name="ttt" /> <input type="checkbox" name="aaa" /> <p>Aaron</p> </div> <script type="text/javascript"> //最简单的选择器 var selector = 'div.aaron,div > p'; var filter = { CLASS: function(className) {}, TAG: function(nodeNameSelector) {} } var matchExpr = { CLASS: /^\.((?:\\.|[\w-]|[^\x00-\xa0])+)/, TAG: /^((?:\\.|[\w*-]|[^\x00-\xa0])+)/ } //层级选择器 var rcombinators = /^[\x20\t\r\n\f]*([>+~]|[\x20\t\r\n\f])[\x20\t\r\n\f]*/; var rtrim = /^[\x20\t\r\n\f]+|((?:^|[^\\])(?:\\.)*)[\x20\t\r\n\f]+$/g; //sizzle的分组方式 //分组 var rcomma = /^[\x20\t\r\n\f]*,[\x20\t\r\n\f]*/; //TAG var TAG = /^((?:\\.|[\w*-]|[^\x00-\xa0])+)/; $('button').click(function(){ var type; var soFar = selector; var match; var matched; var tokens; var groups = []; while (soFar) { //第一大块,分组关系 //查找最左边的选择是不是逗号开头 //matched用于处理第一进入 //因为div input,div p 开始分解,第一个不是特殊符号 if (!matched || (match = rcomma.exec(soFar))) { if (match) { // Don't consume trailing commas as valid soFar = soFar.slice(match[0].length) || soFar; } groups.push((tokens = [])); } //退出处理 matched = false; //第二大块,层级关系 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 filter) { if ((match = matchExpr[type].exec(soFar))) { matched = match.shift(); tokens.push({ value: matched, type: type, matches: match }); soFar = soFar.slice(matched.length); } } if (!matched) { break; } } alert(groups) }) </script> </body> </html>