Creating Your Own Selector Engine – Part 2

May 03, 2010 MaXPert 4 Comments

So in last tutorial we made up some really basic mechanical stuff to get nodes and filter them. The missing pieces of the puzzle were treversing up, down or among siblings. Today we are going to build that part up, plus we will enhance some of previous stuff to make our selector really cross-browser. You can have a look at this page for a detailed list of operations supported across different browsers; so for example, you may discover that in last tutorial I used tagName in ofTag which is not recommended. Looking at the quirksmode list you may end up with ofTag function which looks similar to this new version:

Now our next problem is to come up with a good solution to collect all nodes under a set of given nodes. Sounds pretty simple (you can simply select all nodes under each node using Selector.getAll(…) implemented previously). But the crux of the situation is that a given set of nodes, they may have overlapping children nodes among them. For example you are given a body and a paragraph node as input, where the paragraph node lies within the body node. Now each node that lies under paragraph lies in body as well, so if we have already collected nodes of body, we should not repeat the nodes of paragraph in returned collection. There are many solutions to this problem, the famous Sizzle uses a subroutine Sizzle.uniqueSort to sort and remove duplicates. I am big fan of Sly and Peppy selectors. The approach we are going to use is pretty simple inspiring its idea from classic count sort, thats found in Sly and Peppy.
I am going to write a subroutine that when given array of new elements, and collection of previous elements returns only the new nodes from the array; here is the updated code of Selector with added to function “fetchOnlyNew”:

Here “GUID” is a global unique identifier, which is being applied to each node as __node_id, and incrementing GUID everytime ensures values are unique for each different node. Parameter “previousCollection” will contain a true against the node ID of that collected node if it has been already collected. So, now for example if I implement “.all()” function for our DOMNodes it will look something like this:

So each time for each node “i” (this.nodes[i]) I get all of nodes under it ( Selector.getAll(this.nodes[i]) ), send it to “fetchOnlyNew” with a “collectedNodes” object that is initially empty ( Selector.fetchOnlyNew(Selector.getAll(this.nodes[i]), collectedNodes) ), and finally appending its outcome to nodes list. Pretty simple. I can employee same technique anywhere to get unique collected nodes. So for example lets add “.children()” (That gives me a collection of all the children nodes under set of given nodes), and our DOMNodes class will look something like this now:

Pretty neat. So now I can do queries like this:

Pretty straight forward and readable ( same style as that of jQuery ). Now that we have freedom to do more we can add more functionalities for DOMNodes here is a brief list of all those functionalities (offcourse you can add your own to it):

  • Filter nodes of particular ID (can be done through previous withAttr)
  • Get parent of each node
  • Get next sibling of each node
  • Get previous sibling of each node
  • Union of two DOMNodes
  • Provide with regular expression match to withAttr

I am just giving out the implementation for ones listed above and you can work out more of them at your own:

Now lets finally define a global variable ( eeeekhhhhh I know its a bad practice but this is just for fun ) named “select” that contains DOMNodes for docucument so:

Now we can have fun with our engine. Try queries in programming style:

As a example you can further add allNext, allPrev, contains etc. Next we will be covering some regular expression stuff to parse out a simple CSS query and then we will finally merge these two. So stay tune in soon.

About the author: MaXPert

Expert & Freelancer in PHP, MYSQL, Javascript, including many frameworks like codeigniter, cakephp, mootools, jquery, prototype js and a long list.