javascript CSS Selector engine.

CSS selecotor engine is a relative , new developement in the world of javascript but they've taken the world of libraries by storms.

 

 

The premise behind the engie is that eyo ucan feed it in a CSS selecor (for example "div >span") and will return all the DOM elements that match the selector on the page. 

 

 

There are three way to implement a CSS selector engine, they are

 

 

  • W3C selector APIs
  • XPath 
  • Pure DOM  

 

Selector API 

 

 

First it is the Selecotr API. 

 

QuerySelector

QuerySelecotAll

 

 

Below shows the API that you can use to query the CSS selector engine.

 

for simplicity all the test cases and the .js file are embedded in the following file.

 

 

/**************************************
*@Summary
* cssselectorapi.js
*
* CSS selector api has the api which featured by the api called querySelectorAll and querySelector
*
* querySelectorAlll:  Accepts a CSS selector string and returns a static NodeList of all elements found by the selector
*  querySelector: Accepts a CSS selector string and returns the first element found (or null if no matching element is found).
* @Usage
*   
* @TODO:
* test it 
***************************************/

(function () {
  var count = 1;
  this.rootedQuerySelectorAll = function (elem, query) {
    var oldId = elem.id;
    elem.id = "rooted" + (count++);

    try {
      // is this the reason why the element-rooted query will be turned into some rooted queries?
      // still not quite sure about this point.
      alert("the new id is = " + "#" + elem.id + " " + query);
      return elem.querySelectorAll("#" + elem.id + " " + query);
    }
    catch (e) {
      throw e;
    }
    finally {
      elem.id = oldId;
    }
  };
})();
 

 

below is the test html file.

 

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <script type="text/javascript" src="../unit.js"></script>
    <script type="text/javascript" src="cssselectorapi.js"></script>
    <script type="text/javascript">
      window.onload = function () {
        test("test the CSS selector api ", function () {
          var divs = document.querySelectorAll("body > div");
          assert(divs.length === 2, "Two divs found using a CSS selector.");

          // from the test result, it seems that some of querySelecotr based on the 
          // relative - not rooted, will have different behaviors, as in IE, the following is not 
          // displayed on the IE screen
          var b = document.getElementById("test").querySelector("b:only-child");
          assert(b, "The body element was found relateive to another element");
        });

        test("test the CSS selector on element-rooted query with CSS query API", function () {
          var b = document.getElementById("test").querySelector("div b");
          var blist = document.getElementById("test").querySelectorAll("div b");

          assert(b, "Only the last part of selector matters");
          assert(blist.length === 1, "it returns elements which shall not be returned");
        });


        test("test the CSS selector on element-rooted queries into rooted queries", function () {
          var b = rootedQuerySelectorAll(document.getElementById("test"), "div b");
          assert(b.length === 0, "The selector is now rooted properly.");
        });
      };
    </script>
    <style> 
      #results li.pass { color: Green }
      #results li.fail { color: Red}
    </style>
</head>
<body>
<div id="test">
  <b> hello </b>, I'm a ninja
</div>
<div id="test2"></div>

<ul id="results" />
</body>
</html>

 

 

 

Let me explain something point about the test.

 

 

1.  var divs = document.querySelectorAll("body > div");

          assert(divs.length === 2, "Two divs found using a CSS selector.");

          // from the test result, it seems that some of querySelecotr based on the 
          // relative - not rooted, will have different behaviors, as in IE, the following is not 
          // displayed on the IE screen
          var b = document.getElementById("test").querySelector("b:only-child");
          assert(b, "The body element was found relateive to another element");
 

 this is how you use the CSS selector api, it give the first impression of how the CSS Selector API is used.  

 

 

as explained in the docs, there are different behavior across different browers. and the difference comes as called by the book of ninja is "Selector APIs is more closely capitulates to the existing CSS selecotr engine implementation rathar thant the implementation that were first created by JavaScript engine".

 

 

2. there is know issue access element-rooted query. 

 

 

        test("test the CSS selector on element-rooted query with CSS query API", function () {
          var b = document.getElementById("test").querySelector("div b");
          var blist = document.getElementById("test").querySelectorAll("div b");

          assert(b, "Only the last part of selector matters");
          assert(blist.length === 1, "it returns elements which shall not be returned");
        });
 

 

the problem is that with the element rooted query, only the last part of query is important. so even if there is no 'div' under the div element, it still return a item doing the query 

 

3. to work around the issue, you have the following. 

 

 

(function () {
  var count = 1;
  this.rootedQuerySelectorAll = function (elem, query) {
    var oldId = elem.id;
    elem.id = "rooted" + (count++);

    try {
      // is this the reason why the element-rooted query will be turned into some rooted queries?
      // still not quite sure about this point.
      alert("the new id is = " + "#" + elem.id + " " + query);
      return elem.querySelectorAll("#" + elem.id + " " + query);
    }
    catch (e) {
      throw e;
    }
    finally {
      elem.id = oldId;
    }
  };
})();
 

 

maybe the most interesting part of the workaround is to construct a new ID that is composed of a leading '#rooted', and one unique number to make the id different each call, and last the header is appended to the real query . Now could not figure out how this is possible, but it seems a possibility.

 

 

XPath based impl

 

 

 

 

 

DOM basded Impl

 

DOM basd impl is the most compliant way of impl for CSS selector engine. it has the following benefit.

 

 

  • works on old browsers, such as IE 6 and 7
  • backward compatibility 
  • For speed (some of the selectors are faster in pure DOM ) 

following is one of DOM based CSS selector function .

 

 

 

 

/**************************************
*@Summary
* cssselectordom.js
*  this is one implementation uses the DOM model to implements the CSS selector API.
*  
* 
* @Usage
*   
* @TODO:
* test it 
***************************************/

function find(selector, root) {
  root = root || document;

  var parts = selector.split(" "),
           query = parts[0],
           rest = parts.slice(1).join(" "),
           elems = root.getElementsByTagName(query),
           results = [];

  for (var i = 0; i < elems.length; i++) {
    if (rest) {
      results = results.concat(find(rest, elems[i]));
    }
    else {
      results.push(elems[i]);
    }
  }

  return results;
}
 

 

and below is the code that test it .

 

 

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <!--
     this demonstrate the use of the selector  based on the dom object model , which uses the getElementsByTagName(query) and others. 
    
     -->
     <script type="text/javascript" src="cssselectordom.js"></script>
     <script type="text/javascript" src="../unit.js"></script>
     <script type="text/javascript" >
       window.onload = function () {

         test("Test the CSS selector based implementation based on DOM", function () {
           var divs = find("div");
           assert(divs.length === 2, "Correct number of divs found");
           
           var divs = find("div", document.body);
           assert(divs.length === 2, "Correct number of divs found in the body");

           var divs = find("body div");
           assert(divs.length === 2, "Correct number of divs found in the body");

           var spans = find("div span");
           assert(spans.length === 2, "A duplicate span was found.");
         });
       };
     </script>

     <style type="text/css">
       #results li.fail{ color: Red}
       #results li.pass{color: Green}
     </style>
</head>
<body>

<div>
  <div>
    <span>Span</span>
  </div>
</div>
<ul id="results"></ul>
</body>
</html>

 

 

 

maybe the most interesting part of the workaround is to construct a new ID that is composed of a leading '#rooted', and one unique number to differentiate in each all of the method.

 

 

there is an alternative impl of the find method, this time it use getElementsByTagNames, and it will compare the parent's node until there is a match. 

 

here is the code. 

 

function bottomupFind(selector, root) {
  root = root || document;

  var parts = selector.split(" "),
  query = parts[parts.length - 1],

  rest = parts.slice(0, -1).join(" ").toUpperCase(),
  elems = root.getElementsByTagName(query),
  results = [];

  for (var i = 0; i < elems.length; i++) {
    if (rest) {
      var parent = elems[i].parentNode;
      while (parent && parent.nodeName != rest) { // you may differentiate the difference between the name and the nodeName properties
        parent = parent.parentNode;
      }
      if (parent) {
        results.push(elems[i]);
      }
    } else {
      results.push(elems[i]);
    }
  }
  return results;
}
 

 

and below is the test suite. 

 

         test("Test the CSS selector with a bottom up DOM impl", function () {
           var divs = bottomupFind("div");
           assert(divs.length === 2, "Corrent number of divs found.");

           var divs = bottomupFind("div", document.body);
           assert(divs.length === 2, "Corrent number of divs found.");

           var divs = bottomupFind("body div");
           assert(divs.length === 2, "Correct of vis found in body");

           var spans = bottomupFind("div span");
           assert(spans.length === 1, "No duplicate span was found");

         });
 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值