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
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.
DOM basd impl is the most compliant way of impl for CSS selector engine. it has the following benefit.
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"); });