位置偽類好像天生克從右至左的選取器,處理起來非常複雜,大抵有以下三種情況
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)的選擇順序。不用說,我們又需要一個強大的正則來處理它。