我想,我是国内最熟悉CSS选择器运作机理的人了。新的一年,也该是时间把曾经走过的足迹记录下来,让大家明白,所有成功都是来之不易的。
注:下面给的链接,许多都可能打不开,因为我并没有把它们公开出来。
- 2009年7月24日发表《document.getElementsByClassName的理想实现》,这是我第一篇与选择器相关的文章,动机是出于我博客的代码运行框,我需要选取那些具有特殊类名的textarea与button。
- 2009年10月14日发表《javascript contains方法》,contains方法是用于后代选择器与通配符选择器的,但当时我是为何研究它呢?我也忘记了。
- 2009年10月27日发表《getElementsByAttribute》,这时我已经萌发自己搞一个选择器的念头了。
- 2009年10月28日完成《getElementsByPseudo(私藏版)》,搞定子元素过滤伪类的表达式算法及其他CSS3伪类,因为是私藏版所以让它继续私藏吧。
- 2009年11月25日发表《IE6的getElementById bug》,发现原来原生选择器也是不可靠的。
- 2009年12月1日完成《我的类库 第一次整合》,这是我框架的稚形,里面包含我第一个完整的选择器,当时已支持大部分jQuery自定义伪类了。
- 2009年12月21日完成《我的类库 选择器第1版》,决定将选择器独立出来发展。
- 2010年1月16日发表《CSS Selector の最大の欠点》,这是日本同行对querySelectorAll的局限性研究。
- 2010年1月16日完成《我的选择器 第二版 》。
- 2010年1月17日完成《我的选择器第2.1版 》。
- 2010年1月22日完成《我的选择器第2.2版 》。
- 2010年1月23日完成《我的选择器第2.3版 》。
- 2010年1月23日完成《让IE5+支持DOMNodeInserted事件 》,这为选择器开发的新技术,原理是用HTC的onafterupdate来模拟,但实际上我一次也没有运用到我的选择器上,因此把它雪藏。
- 2010年1月23日发表《获取祖先元素 》,准确的描述是获取不存在包含关系的祖先节点集合。得到这些祖先后,再去获取他们的后代,就不存在去重问题了。
- 2010年1月24日完成《我的选择器 第2.4版》。
- 2010年1月25日完成《我的选择器 第2.5版》。
- 2010年1月25日完成《我的选择器 第2.6版》。
- 2010年1月26日完成《我的选择器 第2.7版》。
- 2010年1月26日完成《我的选择器 第2.8版》。
- 2010年1月27日发表《新锐选择器query1.0 发布》,这是我的选择器发展史上一个里程碑,狂喜之余我甚至不要脸地拿它到51js那里打点赏。这里特别鸣谢51js的abcd(现在我们群的管理员之一)的正则支持,没有他强大的正则,我的选择器是发展不了这么迅猛的。通过这几个版本,我奠定了切割器与适配器的概念。
- 2010年1月30日发表《Ext的DomQuery学习笔记》,学习一下Ext选择器的编译函数机制。
- 2010年2月9日发表《我的选择器 获得经过标记的没有重复的tagName等于tag的元素集》。
- 2010年2月22日完成《queryScopedSelector》。
- 2010年3月14日完成《javascript uuid技术》,这是选择器最重要的技术之一,恐怕离开UUID,根本无法完成我们现在看到的这些主流选择器。
- 2010年11月17日完成《第二代选择器guru设计图》,重新启动选择器的研究,开发代号为guru。
- 2010年11月25日发表《dom.query v2 发布》,新的架构投入使用,消灭了适配器。
- 2010年12月5日发表《dom.query v3 发布》,新切割器投入使用,但由于其他部分的把控不力与极端化,终成失败品。
- 2010年12月5日发表《选择器切割正则的进化》。
- 2010年12月7日开始,研究从右到左选择,即日搞定五个关系选择器,11日完成代理器与映射集,12日有了候选集的重要概念与相关实现,14日搞定位置伪类。
- 2010年12月15日开始,queryv3.5完成,这是我的选择器发展史上一个里程碑,不过速度很成问题,我就没有放出来丢脸。传输器的构念也在此版本确定。
- 由于queryv3.5让我很受伤,queryv3.6-3.8都走回从左到右选择的老路。
- 2010年12月28日,决定传输器的新形态,重新回到从右到左选择的轨道。
- 2010年12月29日发表《取得祖先元素2》。
- 2010年12月31日,将mootools的slickspeed用javascript重写,更名为queryspeed。
- 2011年1月3日,明确种子选择器的概念,完成伟大的切割器(Cutter the Great)与更轻量的传输器,覆写版本号,将刚完成的queryv3.9覆盖为query3.6。
- 2011年1月7日,queryv3.7(新)完成,引入编译函数机制。
最后附上queryv3.5的源码:
/*
query selector version 3.5
Copyright 2010
Dual licensed under the MIT or GPL Version 2 licenses.
author "司徒正美(zhongqincheng)"
http://www.cnblogs.com/rubylouvre/
*/
(function(window){
var A_slice = Array.prototype.slice;
window.dom = {
UID:1,
oneObject : function(array,val){
var result = {},value = val !== void 0 ? val :1;
for(var i=0,n=array.length;i < n;i++)
result[array[i]] = value;
return result;
},
slice:function(nodes,start,end){
return A_slice.call(nodes,(start || 0),(end || nodes.length))
},
isXML : function(context) {
context = context.ownerDocument || document;
return context.createElement("p").nodeName !== context.createElement("P").nodeName;
},
contains : function(ancestor,node) {
if (node.compareDocumentPosition)
return (node.compareDocumentPosition(ancestor) & 8) === 8;
if (ancestor.contains)
return ancestor.contains(node) && ancestor !== node;
while (node = node.parentNode)
if (node == ancestor) return true;
return false;
},
queryId : function (id, context) {
var el = (context || document).getElementById(id);
return el && [el] || []
},
queryTag : function(tag,context){
var flag_skip = tag !== "*",result = [],els = context.getElementsByTagName(tag);
if(-[1,]){
return A_slice.call(els)
}else{
for(var i = 0,ri = 0,el;el = els[i++];)
if(flag_skip || el.nodeType === 1){
result[ri++] = el
}
}
return result;
},
queryPos : function(selectors,context,flag_xml,name,value){
var filter = dom.$$filters[name],ret = [],recursion = [], i = 0, ri = 0,nodes,node,selector;
do{
selector = selectors.pop();
if(selector != ","){
recursion.unshift(selector)
}else{
selectors.push(selector);
break;
}
}while(selectors.length);
nodes = dom.query(recursion,context,flag_xml);
//如果value为空白则将集合的最大索引值传进去,否则将exp转换为数字
var num = (value === ""|| value === void 0) ? nodes.length - 1 : ~~value;
for (; node = nodes[i];i++){
if(filter.call(node,i, num))
ret[ri++] = node;
}
ret.selectors = selectors
return ret
}
}
var child_pseudo = "first-child|last-child|only-child|nth-child|nth-last-child";
var reg_find =/(^[\w\u00a1-\uFFFF][\w\u00a1-\uFFFF-]*)|^(\*)|^#([\w\u00a1-\uFFFF-]+)|^\.([\w\u00a1-\uFFFF-]+)|^:(root)|^:(link)|^:(eq|gt|lt|even|odd|first[^-]|last[^-])\(?(.*)\)?/;
var reg_swap = /([\w\u00a1-\uFFFF][\w\u00a1-\uFFFF-]*)(\.[\w\u00a1-\uFFFF-]+|:(?!eq|gt|lt|even|odd|first[^-]|last[^-])\S+(?:\(.*\))?|\[[^\]]*\])/g;
var reg_split =/^\s+|[\w\u00a1-\uFFFF][\w\u00a1-\uFFFF-]*|[#.:][\w\u00a1-\uFFFF-]+(?:\([^\)]*\))?|\[[^\]]*\]|(?:\s*)[>+~,*](?:\s*)|\s(?=[\w\u00a1-\uFFFF*#.[:])/g;
var reg_id= /^#([^,#:\.\s\xa0\u3000\+>~\[\(])+$/;
var reg_tag = /^[\w\u00a1-\uFFFF][\w\u00a1-\uFFFF-]*$/;
var reg_pseudo = /^:(\w[-\w]*)(?:\((.*)\))?$/;
var reg_href = /^(?:src|href|style)$/;
var reg_attribute = /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/ ;
var one_child = dom.oneObject((child_pseudo+"|"+child_pseudo.replace(/child/g,"of-type")).split("|"));
var one_position = dom.oneObject("eq|gt|lt|first|last|even|odd".split("|"));
var documentOrder = !-[1,] ? function (a, b) {
return (a.sourceIndex - b.sourceIndex);
}:function (a, b) {
return (3 - (a.compareDocumentPosition(b) & 6));
}
var map_attr = {
"accept-charset": "acceptCharset",
accesskey: "accessKey",
bgcolor: "bgColor",
cellpadding: "cellPadding",
cellspacing: "cellSpacing",
"char": "ch",
charoff: "chOff",
"class": "className",
codebase: "codeBase",
codetype: "codeType",
colspan: "colSpan",
datetime: "dateTime",
defaultchecked:"defaultChecked",
defaultselected:"defaultSelected",
defaultvalue:"defaultValue",
"for": "htmlFor",
frameborder: "frameBorder",
"http-equiv": "httpEquiv",
ismap: "isMap",
longdesc: "longDesc",
maxlength: "maxLength",
marginwidth:"marginWidth",
marginheight:'marginHeight',
nohref: "noHref",
noresize:"noResize",
noshade: "noShade",
readonly: "readOnly",
rowspan: "rowSpan",
tabindex: "tabIndex",
usemap: "useMap",
vspace: "vSpace",
valuetype: "valueType"
};
var queryAttribute = function(el,name,flag_xml){
var special = map_attr[name];
if(!flag_xml && special)
return el[special];
var flag = reg_href.test(name) ? 2 : 0;
return el.getAttribute(name,flag) ;
};
/**************************特征侦探*****************************************/
dom.support = {
sliceNodes : true
};
var HTML = document.documentElement;
var div = document.createElement("div");
HTML.insertBefore(div, HTML.firstChild);
var id = new Date - 0
div.innerHTML = '<a name="'+id+'"></a><b id="'+id+'"></b>';
dom.support.diffName = document.getElementById(id) !== div.firstChild;
try{//检测是否支持
A_slice.call(div.childNodes)
}catch(e){
dom.support.sliceNodes = false;
}
div.appendChild(document.createComment(''))
dom.support.diffComment = div.getElementsByTagName("*").length !== 3;
HTML.removeChild(div)
/************************根据浏览器特征重写部分函数**************************/
if(!dom.support.diffName){
dom.queryId = function(id,root){
root = root || document;
if(root.getElementById){
var el = root.getElementById(id);
return el && el.attributes['id'].value === id ? [el] :[]
} else {
var all = root.all[id];
for(var i=0;el=all[i++];){
if(el.attributes['id'].value === id)
return [el]
}
return []
}
}
}
if(!dom.support.sliceNodes){
dom.slice = function(nodes,start,end){
var i = nodes.length,result = [];
while(i--){
result[i] = nodes[i];
}
return A_slice.call(result,(start || 0),(end || result.length));
}
}
/****************************过滤器*****************************************/
var queryPseudoNoExp = function(name,isLast,isOnly){
var head = "var node = this;{0} while((node=node.{1})) if(node.{2} === {3}) return false;";
var body = "var prev = this;while ((prev = prev.previousSibling)) if (prev.{2} === {3}) return false;"
var foot = "return true;"
var start = isLast ? "nextSibling": "previousSibling";
var fills = {
type : ["var tagName = this.nodeName;",start,"nodeName","tagName"],
child : ["" ,start,"nodeType","1"]
};
var fn = isOnly ? head+body+foot :head+foot;
return new Function(fn.replace(/{(\d)}/g, function($,$1){
return fills[name][$1];
}));
}
var queryPseudoHasExp = function(name,isLast){
var outer = function(a,b){
var el = this,parent = el.parentNode;
if (parent.querytime != dom.querytime ){
undefined
}
var diff = el.queryIndex - b;
if ( a === 0 ) {
return diff === 0;
} else {
return ( diff % a === 0 && diff / a >= 0 );
}
}
var inner = "var {0}; for (var node = parent.{1}; node; node = node.{2}){ if(node.nodeType === 1){ {3} } } parent.querytime = dom.querytime;";
var buildIndexByChild = "node.queryIndex = ++index;"
var buildIndexByType = "tagName = node.nodeName;if(tagName in cache){ ++cache[tagName]; }else{cache[tagName] = 1;}node.queryIndex = cache[tagName];"
var start = isLast ? "lastChild" : "firstChild";
var next = isLast ? "previousSibling":"nextSibling";
var fills = {
type : ["cache = {},tagName",start,next,buildIndexByType],
child : ["index = 0" ,start,next,buildIndexByChild]
}
inner = inner.replace(/{(\d)}/g, function($,$1){
return fills[name][$1];
});
return eval(("[" +outer+"]").replace("undefined",inner))[0];
}
dom.$$filters = { //伪类选择器的过滤器
enabled: function(){//CSS3属性伪类
return this.disabled === false && this.type !== "hidden";
},
disabled: function(){//CSS3属性伪类
return this.disabled === true;
},
checked: function(){//CSS3属性伪类
return this.checked === true;
},
indeterminate:function(){//CSS3属性伪类
return this.indeterminate = true && this.type === "checkbox"
},
selected: function(){//自定义属性伪类
this.parentNode.selectedIndex;//处理safari的bug
return this.selected === true;
},
empty: function () {//CSS3结构伪类(子元素过滤伪类)
return !this.firstChild;
},
link:function(){//CSS2链接伪类
return this.nodeName === "A";
},
lang: function (reg) {//CSS3语言伪类
var el = this;
while (el && el.getAttribute){//如果是文档对象就不用往上找了
if(reg.test(el.getAttribute("lang")))
return true;
el = el.parentNode;
}
},
header: function(){//自定义属性伪类
return /h\d/i.test( this.nodeName );
},
button: function(){//自定义属性伪类
return "button" === this.type || this.nodeName === "BUTTON";
},
input: function(){//自定义属性伪类
return /input|select|textarea|button/i.test(this.nodeName);
},
hidden : function( ) {//自定义可见性伪类
return this.type === "hidden" || (this.offsetWidth === 0 ) || (!-[1,] && this.currentStyle.display === "none") ;
},
visible : function( ) {//自定义可见性伪类
return this.type !== "hidden" && (this.offsetWidth || this.offsetHeight || (!-[1,] && this.currentStyle.display !== "none"));
},
target:function(exp,context){//CSS2.1目标标准
var id = context.location.hash.slice(1);
return (this.id || this.name) === id;
},
parent : function( ) {//自定义结构伪类
return !!this.firstChild;
},
contains: function(exp) {//自定义内容伪类
return (this.textContent||this.innerText||'').indexOf(exp) !== -1
},
has: function( ) {//自定义结构伪类(子元素过滤伪类,根据子节点的选择器情况进行筛选)
for(var i =0,node;node = arguments[i++];)
if(dom.contains(this,node)){
return true;
}
return false;
},
first: function(index){//自定义位置伪类
return index === 0;
},
last: function(index, num){//自定义位置伪类
return index === num;
},
even: function(index){//自定义位置伪类
return index % 2 === 0;
},
odd: function(index){//自定义位置伪类
return index % 2 === 1;
},
lt: function(index, num){//自定义位置伪类
return index < num;
},
gt: function(index, num){//自定义位置伪类
return index > num;
},
eq: function(index, num){//自定义位置伪类
return index === num;
},
not:function(){},//CSS3反选伪类
"nth-child" : queryPseudoHasExp("child",false),//CSS3子元素过滤伪类
"nth-last-child" : queryPseudoHasExp("child",true),//CSS3子元素过滤伪类
"nth-of-type" : queryPseudoHasExp("type",false),//CSS3子元素过滤伪类
"nth-last-of-type": queryPseudoHasExp("type",true),//CSS3子元素过滤伪类
"first-child" : queryPseudoNoExp("child",false,false),//CSS3子元素过滤伪类
"last-child" : queryPseudoNoExp("child",true ,false),//CSS3子元素过滤伪类
"only-child" : queryPseudoNoExp("child",true ,true),//CSS3子元素过滤伪类
"first-of-type" : queryPseudoNoExp("type" ,false,false),//CSS3子元素过滤伪类
"last-of-type" : queryPseudoNoExp("type" ,true ,false),//CSS3子元素过滤伪类
"only-of-type" : queryPseudoNoExp("type" ,true ,true)//CSS3子元素过滤伪类
}
"text|radio|checkbox|file|password|submit|image|reset".replace(/\w+/g, function(name){
dom.$$filters[name] = function(){//自定义属性伪类
return this.type === name;
}
});
/***********************迭代器**************************/
var makeIterator = function(name){
var outer = function(nextset){
var set = this, nodes = this.nodes,tagName = this.tagName, filter = this.filter,args = this.args,
level = this.level, _level, result = [], testee, uid, pid,yess = {};
for(var i = 0,ri = 0, node; node = nodes[i++];){
uid = node.uniqueID || (node.uniqueID = dom.UID++);
testee = set[uid] || node;
undefined;
}
nextset.nodes = result;
}
var parents = "if(level){_level = level; while(_level-- && ( testee = testee.parentNode));if(!testee)break;}"
var fills ={
current : ["" ,""],
border : ["" ,"" ,"previousSibling","" ,"break;"],
borders : ["yess[pid] = ","break;","previousSibling","" ,""],
parent : ["yess[pid] = ","" ,"parentNode" ,"" ,"break;"],
parents : ["yess[pid] = ","break;","parentNode" ,parents,""]
}
var filter = "if((!tagName || tagName === testee.nodeName) && (!filter || filter.apply(testee,args))){\
{0} result[ri++] = node;nextset[uid] = testee;{1} }".replace(/{(\d)}/g, function($,$1){
return fills[name][$1];
});
var inner = "while((testee = testee.{2})){if(testee.nodeType === 1 ){ {3}"+
(name === "border" ? "" : "pid = testee.uniqueID || (testee.uniqueID = dom.UID++);if(yess[pid]){ result[ri++] = node;nextset[uid] = testee; break;}")+"FILTER {4} } }"
if(name === "current"){
return eval(("[" +outer+"]").replace("undefined",filter))[0];
}else{
inner = inner.replace(/{(\d)}/g, function($,$1){
return fills[name][$1];
}).replace("FILTER",filter);
return eval(("[" +outer+"]").replace("undefined",inner))[0];
}
}
var iterators = {
current:makeIterator("current"),
parent:makeIterator("parent"),
parents:makeIterator("parents"),
border:makeIterator("border"),
borders:makeIterator("borders")
}
/***********************适配器*********************************************/
//通过获取每次的过滤器,迭代器等
var adapters = {
"#":function(selector,flag_xml,context,transport){//★★★★(1)ID选择器
transport.args = [selector.slice(1)];
transport.filter = function(id){
return (this.id || this.getAttribute("id")) === id;
}
},
".":function(selector,flag_xml,context,transport){//★★★★(2)类选择器
transport.args = [new RegExp('(?:^|[ \\t\\r\\n\\f])' + selector.slice(1) + '(?:$|[ \\t\\r\\n\\f])')];
transport.filter = function(reg_class){
return reg_class.test(this.className || this.getAttribute && this.getAttribute("class"));
}
},
"[":function(selector,flag_xml,context,transport){//★★★★(3)属性选择器
var match = selector.match(reg_attribute);
transport.args = [match[1], match[2], match[4]];
transport.filter = function(name,operator,value){
var attrib = queryAttribute(this, name, flag_xml);//取得元素的实际属性值
if(!operator)
return attrib !== false && attrib+"";
switch (operator) {
case "=":
return attrib === value;
case "!=":
return attrib !== value;
case "~=":
return (" " + attrib + " ").indexOf(value) !== -1;
case "^=":
return attrib.indexOf(value) === 0;
case "$=":
return attrib.lastIndexOf(value) + value.length === attrib.length;
case "*=":
return attrib.indexOf(value) !== -1;
case "|=":
return attrib === value || attrib.substring(0, value.length + 1) === value + "-";
}
}
},
">":function(selector,flag_xml,context,transport){//★★★★(4)亲子
transport.iterator = iterators.parent;
},
"~":function(selector,flag_xml,context,transport){//★★★★(5)兄长
transport.iterator = iterators.borders;
},
"+":function(selector,flag_xml,context,transport){//★★★★(6)相邻
transport.iterator = iterators.border;
},
" ":function(selector,flag_xml,context,transport){//★★★★(7)后代
transport.iterator = iterators.parents;
},
"*":function(selector,flag_xml,context,transport){//★★★★(8)后代
transport.level = ~~transport.level + 1;
transport.iterator = iterators.parents;
},
":":function(selector,flag_xml,context,transport){//★★★★(9)伪类
var match = selector.match(reg_pseudo), name = match[1],value = match[2]||"",nodes;
if(one_position[name]){//位置伪类
nodes = dom.queryPos(transport.selectors,context,flag_xml,name, value);
transport.selectors = nodes.selectors;
}else if(name === "has"){
transport.args = dom.query(value,context,flag_xml);
transport.filter = dom.$$filters[name];
var nextset = {}
transport.iterator(nextset);
transport.nodes = nextset.nodes;
delete transport.filter;
return
}else if( name ==="not"){
nodes = dom.query(value,context,flag_xml);
}else{
if(name==="lang")
transport.args = [new RegExp("^" + value, "i")];
if(one_child[name]){
match = (value === "even" && "2n" || value === "odd" && "2n+1" || value.replace(/\s/g,"").replace(/(^|\D+)n/g,"$11n")).split("n");
transport.args = [~~match[0],~~match[1]];
}
transport.filter = dom.$$filters[name];
return
}
if(nodes){
for(var i = 0, hash = {}, uid , node;node = nodes[i++];){
uid = node.uniqueID || (node.uniqueID = dom.UID++);
hash[uid] = node;
}
transport.args = [hash,name === "not"];
transport.filter = function(hash,not){
return hash[this.uniqueID] ^ not
}
}
}
}
var adapterOfTag = function(selector,flag_xml,context,transport){
selector = flag_xml ? selector : selector.toUpperCase();
transport.tagName = selector
}
/*************************获取候选集***************************/
var getCandidates = function(selectors,context,flag_xml,transport){
var selector = selectors.pop(), match = selector.match(reg_find), nodes, node;
if(match){
if(match[7]){//位置伪类
nodes = dom.queryPos(selectors,context,flag_xml, match[7], match[8]);
}else if(match[1] || match[2] ){//标签或通配符选择器
nodes = dom.queryTag(match[1] || match[2],context);
}else if(match[3] && context.getElementById){//ID选择器
node = context.getElementById(match[3]);
nodes = node && [node] || [];
}else if(match[4] && context.getElementsByClassName){//类选择器
nodes = dom.slice(context.getElementsByClassName(match[4]));
}else if(match[5] && context.documentElement){//根伪类
nodes = [context.documentElement];
}else if(match[6] && context.links){//链接伪类
nodes = dom.slice(context.links);
}
}
if(!nodes){
nodes = dom.queryTag("*",context);
selectors.push(selector);
}
transport.selectors = "selectors" in nodes ? nodes.selectors : selectors;
transport.nodes = nodes;
transport.iterator = iterators.current;
}
dom.query = function(selectors, context,flag_xml){
dom.querytime = new Date-0;
context = context || document;
if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
return [];
}
flag_xml = flag_xml !== void 0 ? flag_xml : dom.isXML(context);
var result = [],uniqResult = {}, transport = {},parts = [],
selector, ri = 0, i = -1, nodes, node, flag_sort ,uid;
if (typeof selectors === "string" ) {
selectors = selectors.replace(/^[^#\(]*(#)/, "$1");
if(arguments.length === 1){
if(reg_id.test(selectors))
return dom.queryId(selectors.slice(1),context);
if(reg_tag.test(selectors)){
return dom.queryTag(selectors,context);
}
}
//将标签选择器与紧跟在它后面的非位置伪类、类、属性相调换
selectors = selectors.replace(reg_swap, "$2^$1");
//将选择器群组转换为数组
selectors.replace(reg_split,function(part){
i++
if(part == false ){//如果为空白字符串
if(i)
parts[ri++] = " ";//并且并不是第一个捕获的
}else if(part.match(/^\s*([>+~,*])\s*$/)){
parts[ri++] = RegExp.$1;
}else {
parts[ri++] = part;
}
});
selectors = parts;
}
//将候选集与选择器数组与下一次要使用的迭代器附于传送器上
getCandidates(selectors,context,flag_xml,transport);
selectors = transport.selectors;
//transport的生存周期从上一次甄选操作到下一次甄选操作
while(selectors.length){
selector = selectors.pop();
if(selector === ","){
result = result.concat(transport.nodes);//开始一下选择器群组做准备
getCandidates(selectors,context,flag_xml,transport);
selectors = transport.selectors ;
flag_sort = true;
}else{
(adapters[selector.charAt(0)] || adapterOfTag)(selector,flag_xml,context,transport) ;
selectors = transport.selectors || selectors;
if(transport.filter || transport.tagName){
transport.iterator(uniqResult);//返回新的传输器(兼映射集),它里面附有节点集
transport = uniqResult;
uniqResult = {};//这是新的传输器
}
}
}
result = result.concat(transport.nodes);
if(result.length > 1 && flag_sort){
i = ri = 0, nodes= [];
//减少候选集的个数再进行排序
for(;node = result[i++];){
uid = node.uniqueID || (node.uniqueID = dom.UID++);
if (!uniqResult[uid]){
uniqResult[uid] = nodes[ri++] = node;
}
}
result = nodes.sort(documentOrder);
}
return result;
}
})(this);