1 <div>
2 <div>
3 <span>Span</span>
4 </div>
5 </div>
6
7 window.onload = function(){
8 function find(selector, root){
9 root = root || document;
10
11 var parts = selector.split(""),
12 query = parts[0],
13 rest = parts.slice(1).join(""),
14 elems = root.getElementsByTagName( query ),
15 results = [];
16
17 for ( var i = 0; i < elems.length; i++ ) {
18 if ( rest ) {
19 results = results.concat( find(rest, elems[i]) );
20 } else {
21 results.push( elems[i] );
22 }
23 }
24
25 return results;
26 }
27
28 var divs = find("div");
29 assert( divs.length === 2, "Correct number of divs found." );
30
31 var divs = find("div", document.body);
32 assert( divs.length === 2, "Correct number of divs found in body." );
33
34 var divs = find("body div");
35 assert( divs.length === 2, "Correct number of divs found in body." );
36
37 var spans = find("div span");
38 assert( spans.length === 2, "A duplicate span was found." );
39 };
1 var selector = "div.class > span:not(:first-child) a[href]"
2 var chunker = /((?:\([^\)]+\)|\^\+\]|[^ ,\(\[]+)+)(\s*,\s*)?/g;
3 var parts = [];
4
5 // Reset the position of the chunker regexp (start from beginning)
6 chunker.lastIndex = 0;
7
8 // Collect the pieces
9 while ( (m = chunker.exec(selector)) !== null ) {
10 parts.push( m[1] );
11 // Stop if we've countered a comma
12 if ( m[2] ) {
13 extra = RegExp.rightContext;
14 break;
15 }
16 }
17
18 assert( parts.length == 4, "Our selector is broken into 4 unique parts." );
19 assert( parts[0] === "div.class", "div selector" );
20 assert( parts[1] === ">", "child selector" );
21 assert( parts[2] === "span:not(:first-child)", "span selector" );
22 assert( parts[3] === "a[href]", "a selector" );
代码清单2 A regular expression for breaking apart a CSS selector
然而我们刚刚的实现还过于简单,在刚才返回的数组中错误得存在着两个span元素而不是一个.所以为了使选择器引擎返回的结果中没有重复的元素,我们需要做额外的校验,大多数TD选择器为了确保返回结果的唯一性都实现了不同的校验方法.
但对于验证元素在集合中的的唯一性,DOM本身没有为我们提供较为便捷的方法,这迫使我们必须自己实现,在这里我们逐一遍历每个元素,为每一个元素添加一个临时的uniqueID作为属性,标记该元素我们先前已处理过.
1 <div id="test">
2 <b>Hello</b>, I'm a ninja!
3 </div>
4 <div id="test2"></div>
5
6 (function(){
7 var run = 0;
8 this.unique = function( array ) {
9 var ret = [];
10 run++;
11 for ( var i = 0, length = array.length; i < length; i++ ) {
12 var elem = array[ i ];
13 if ( elem.uniqueID !== run ) {
14 elem.uniqueID = run;
15 ret.push( array[ i ] );
16 }
17 }
18 return ret;
19 };
20 })();
21
22 window.onload = function(){
23 var divs = unique( document.getElementsByTagName("div") );
24 assert( divs.length === 2, "No duplicates removed." );25 var body = unique( [document.body, document.body] );
26 assert( body.length === 1, "body duplicate removed." );
27 };
代码清单3 Finding the unique elements in an array.
此类方法为数组中的每一个元素添加了一个额外的属性,标记他们已经被访问过,返回的结果是一个不包含重复元素的数组,在几乎所有javaScript库中都能找到类似的方法.
如果你觉得验证元素的重复性是一件很麻烦的工作,这里还有另外一套选择器引擎方案.
BU(自底向上)引擎,它对css选择器的处理完全与TD引擎相反,举例来说,对给定的css选择器'div span',它会首先在页面中查找所有span元素,随后追溯每一个span元素的祖先元素,并从中查找div元素.
这种选择器引擎的应用并没有其他引擎来的广泛,尽管用它对一些简单的选择器处理可能非常快捷.但是由于对元素祖先节点的追溯和确认是一件十分耗时并且难以测量其规模的工作. 但无论怎样,这种简单的选择器引擎还是给我们提供了一套可以考虑的实现方案.
1 <div>
2 <div>
3 <span>Span</span>
4 </div>
5 </div>
6
7 window.onload = function(){
8 function find(selector, root){
9 root = root || document;
10 var parts = selector.split(""),
11 query = parts[parts.length - 1],
12 rest = parts.slice(0,-1).join("").toUpperCase(),
13 elems = root.getElementsByTagName( query ),
14 results = [];
15 for ( var i = 0; i < elems.length; i++ ) {
16 if ( rest ) {
17 var parent = elems[i].parentNode;
18 while ( parent && parent.nodeName != rest ) {
19 parent = parent.parentNode;
20 }
21 if ( parent ) {
22 results.push( elems[i] );
23 }
24 } else {
25 results.push( elems[i] );
26 }
27 }
28 return results;
29 }
30
31 var divs = find("div");
32 assert( divs.length === 2, "Correct number of divs found." );
33 var divs = find("div", document.body);
34 assert( divs.length === 2, "Correct number of divs found in body." );
35 var divs = find("body div");
36 assert( divs.length === 2, "Correct number of divs found in body." );
37 var spans = find("div span");
38 assert( spans.length === 1, "No duplicate span was found." );
39 };
代码清单4 a simple bottom up selector engine
代码清单4显示了一个简单的BU引擎的内部结构,请注意它的祖先节点查询深度仅为1,若想要对DOM树做更为全面而深层次的查找,就必须对当前节点的深度值进行跟踪并记录,这样便需要两个记录状态的数组,其中一个数组保存需要返回的元素集合,另外一个数组则保存与当前测试节点相关的所有元素.
原文来自: John Resig - [秘密 of the 被阉割的 javascript 忍者] [Charpter 10 - Css Selector Engine]