位置伪类好像天生克从右到左的选择器,处理起来非常复杂,大抵有以下三种情况
1、位置选择器出现群组的最后面。这种情况相对简单,我们先跳过它,取得节点后再进行过滤,如“"div span:eq(0)”
for (var selector ; selector = selectors[i++];) {
/**********略**********/
/*遍历DOM树,收集符合条件的节点*/
}
/*如果选择器群组包含位置伪类,则进行筛选*/
if(pos && obj.nodes.length ){
obj.nodes = posFilter(obj.nodes,pos);
}
posFilter函数:
var posFilter = function(nodes,pos){
var match = pos.match(reg_pseudo), name = match[1],value = match[3],node,ri = 0,i = 0,result = [],
filter = dom._filters[name]
value = (value === ""|| value === void 0) ? nodes.length - 1 : ~~value;
for (; node = nodes[i];){
if(filter(i++, value) )
result[ri++] = node;
}
return result;
}
2、位置伪类位于并联选择器的两边,如“div:eq(0),#aa.class”
if(selector === ","){
if(pos && obj.nodes.length ){//处理位置伪类
obj.nodes = posFilter(obj.nodes,pos)
pos = false;
}
result = result.concat(obj.nodes);
//处理逗号另一边的选择器群组
match = getCandidates(selectors,context,i);
i = match[1];
pos = match[2];
if(!match[0].length){
i = indexOf(selectors,",",i);
if(i===-1){
break;
}
}
obj = {
set:{},
proxy:proxyViaSelf,
nodes:match[0]
}
flag_sort = true;
}
如果位置伪类出现在中间并且不靠近并联选择器,如“form span:eq(1) strong”。毫无疑问,我们只能用递归收拾它了。
//比如form span:eq(1) strong
":":function(selector,obj,flag_xml,flag_not,doc,selectors,i){//★★★★(9)伪类
var match = selector.match(reg_pseudo), name = match[1];
if(one_position[name]){//位置伪类
var last = indexOf(selectors,",",i);
last = last === -1 ? selectors.length :last;
obj.args = [dom.query(selectors.slice(i-1,last),doc,flag_xml,flag_not)];//我们在这里筛选出匹配“form span:eq(1)”的节点
obj.filter = function(nodes){//过滤器,用于strong与eq(0)之间的后代选择器对应的代理器
return indexOf(nodes,this) > -1;
},
obj.i = last;
}
}
2010.12.16日补充
如果我们对原来的选择器群组做一些调整,或者能简化这逻辑,如div:eq(0)变成:eq(0)^div,那么从右到左选择时,自然成为div,:eq(0)的选择顺序。不用说,我们又需要一个强大的正则来处理它。