matcher就是elementMatcher函数的包装,整个匹配的核心就在这个里面了:
function( elem, context, xml ) { var i = matchers.length; while ( i-- ) { if ( !matchers[i]( elem, context, xml ) ) { return false; } } return true; }
我们先来回顾下这个matchers的组合原理,这个地方是最绕的,也是最晕的,所以还是要深入的理解才行哦。
先上个简单的流程图:
执行分解:
第一步:
div > p + div.aaron input[type="checkbox"]
从右边剥离出原生API能使用的接口属性
context.getElementsByTagName( input )
所以找到了input,因为只可以用tag是查询,但是此时结果是个合集,引入seed的概念,称之为种子合集。
第二步:
div > p + div.aaron [type="checkbox"]
重组选择器,踢掉input,得到新的tokens词法元素哈希表。
第三步:
通过matcherFromTokens函数,然后根据关系选择器 【">","空","~","+"】拆分分组,因为DOM中的节点都是存在关系的,所以引入Expr.relative -> first:true 两个关系的“紧密”程度,用于组合最佳的筛选。
一次按照如下顺序解析并且编译闭包函数,编译规则:div > p + div.aaron [type="checkbox"]。
编译成4组闭包函数,然后在前后在合并组合成一组:
div > p + div.aaron input[type="checkbox"]
先看构造一组编译函数
A: 抽出div元素,对应的是TAG类型
B: 通过Expr.filter找到对应匹配的处理器,返回一个闭包处理器
如TAG方法:
"TAG": function( nodeNameSelector ) { var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); return nodeNameSelector === "*" ? function() { return true; } : function( elem ) { return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; }; },
C:将返回的curry方法放入到matchers匹配器组中,继续分解
D:抽出子元素选择器 '>' ,对应的类型 type: ">"
E:通过Expr.relative找到elementMatcher方法分组合并多个词素的的编译函数
function(elem, context, xml) { var i = matchers.length; while (i--) { if (!matchers[i](elem, context, xml)) { return false; } }
所以这里其实就是执行了各自Expr.filter匹配中的的判断方法了,看到这里matcher方法原来运行的结果都是bool值,所以这里只返回了一个组合闭包,通过这个筛选闭包,各自处理自己内部的元素。
F:返回的这个匹配器还是不够的,因为没有规范搜索范围的优先级,所以这时候还要引入addCombinator方法
G:根据Expr.relative -> first:true 两个关系的“紧密”程度
如果是亲密关系addCombinator返回:
function( elem, context, xml ) { while ( (elem = elem[ dir ]) ) { if ( elem.nodeType === 1 || checkNonElements ) { return matcher( elem, context, xml ); } } }
所以可见如果是紧密关系的位置词素,找到第一个亲密的节点,立马就用终极匹配器判断这个节点是否符合前面的规则。
<!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 id="test1">超级匹配器,点击选中checked</button> <div id="text"> <div class="Aaron"> <input type="checkbox" name="readme" /> <input type="checkbox" name="ttt" /> <input type="checkbox" name="aaa" /> <p>Sizzle</p> </div> </div> <script type="text/javascript"> var filter = { ATTR: function(name, operator,check) { return function(elem) { var attr = elem.getAttribute(name) if (operator === "=") { if(attr === check){ return true } } return false; } }, TAG: function(nodeNameSelector) { return function(elem) { return elem.nodeName && elem.nodeName.toLowerCase() === nodeNameSelector; }; } } function addCombinator(matcher) { return function(elem, context, xml) { while ((elem = elem['parentNode'])) { if (elem.nodeType === 1) { //找到第一个亲密的节点,立马就用终极匹配器判断这个节点是否符合前面的规则 return matcher(elem); } } } } 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]; } function matcherFromTokens(tokens){ var len = tokens.length; var matcher, matchers = []; for (i = 0; i < len; i++) { if (tokens[i].type === " ") { matchers = [addCombinator(elementMatcher(matchers), matcher)]; } else { matcher = filter[tokens[i].type].apply(null, tokens[i].matches); matchers.push(matcher); } } return elementMatcher(matchers); } /** * 超级匹配器 * @return {[type]} [description] */ function matcherFromGroupMatchers(elementMatchers) { var superMatcher = function(seed) { var results = []; var matcher, elem; for (var i = 0; i < seed.length; i++) { matcher = elementMatchers[0]; var elem = seed[i]; if (matcher(elem)) { results.push(elem); break; } } return results; }; return superMatcher; } function compile() { //种子合集 var seed = document.querySelectorAll('input') //选择器 var selector = "Aaron [name=ttt]"; var elementMatchers = []; var match = [{ matches: ["div"], type: "TAG", value: "Aaron" }, { type: " ", value: " " }, { matches: ["name", "=", "ttt"], type: "ATTR", value: "[name=ttt]" }] elementMatchers.push(matcherFromTokens(match)); //超级匹配器 var cached = matcherFromGroupMatchers(elementMatchers) results = cached(seed) results[0].checked = 'checked' } $('#test1').click(function() { compile(); }) </script> </body> </html>