Step Debugging jQuery Selectors

Posted July 16, 2009 at 11:00 AM by Ben Nadel

Tags: Javascript / DHTML

NOTE: This blog post was inspired by a post that Ray Camden did a while back about an AIR-based jQuery selector tester.

I was talking to some people a while back about debugging jQuery selectors. To me, the hardest part about debugging jQuery selectors is that when they are not returning the set of nodes you want, or nodes at all, its hard to tell where you went wrong. As such, I thought it would be really cool to build a little jQuery selector test harness that would step through the jQuery selector search path, one step at a time, highlighting each intermediary set of nodes in the process. This way, you can see where in the path you went wrong.

 
 
 
 
 
 
 
 
 
 

As you can see, the final nodes that would be returned by your target jQuery selector are highlighted in Gold. If your jQuery selector doesn't match any nodes, all you will see are shades of gray.

Since this blog post isn't so much about the code, but rather the overall functionality, I'll just display the code below without going in depth into what it does. I wrote the code in haste and am not going to stand by its "cleanliness".

The HTML Page:

  • <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  • <html>
  • <head>
  • <title>Untitled</title>
  • <script src="jquery-1.3.2.js" type="text/javascript"></script>
  • <script src="trace.js" type="text/javascript"></script>
  • <script type="text/javascript">
  •  
  • $(
  • function(){
  • var jForm = $( "#tester" );
  • var jText = jForm.find( ":text" );
  • var jButton = jForm.find( ":button" );
  • var objTester = new SelectorTester( 1000 );
  •  
  • // Hook up form submit.
  • jForm.submit(
  • function(){
  • // Run test.
  • objTester.Test( jText.val() );
  •  
  • // Kill form submission.
  • return( false );
  • }
  • );
  • }
  • );
  •  
  • </script>
  • </head>
  • <body>
  •  
  • <form id="tester">
  • <p>
  • <input type="text" size="40" />
  • <input type="submit" value="Run Test" />
  • </p>
  • </form>
  •  
  •  
  • <h1>
  • jQuery Selector Testing
  • </h1>
  •  
  • <p>
  • This is the jQuery selector tester. The way it works is
  • that you enter your selector in the above form and hit
  • <strong>"Run Test"</strong>. The tester then parses your
  • selector into its sub-parts and runs each sub-part
  • individually.
  • </p>
  •  
  • <p>
  • The beauty of this is that it highlights each intermediary
  • node collection and then <em>pauses</em> before moving
  • onto the next intermediary collection. This gives you the
  • ability to trace the route of the selector.
  • </p>
  •  
  • <div>
  •  
  • <p>
  • I am in a <span>DIV > P</span>
  • </p>
  •  
  • <p>
  • I am in a DIV > P
  • </p>
  •  
  • </div>
  •  
  • </body>
  • </html>

The Javascript code behind:

  • // I am the base class for the testing.
  • function SelectorTester( intPause ){
  • this.OriginalSelector = "";
  • this.Pause = (intPause || 1000);
  • this.Selectors = [];
  • this.Timeout = null;
  • }
  •  
  •  
  • // I clean the selectore before parsing.
  • SelectorTester.prototype.CleanSelector = function( strSelector ){
  • // Trim selector.
  • strSelector = $.trim( strSelector );
  •  
  • // Make sure we only ever have single spaces.
  • strSelector = strSelector.replace(
  • new RegExp( " +", "g" ),
  • " "
  • );
  •  
  • // Hook up the child selector with next selector.
  • strSelector = strSelector.replace(
  • new RegExp( "> +", "g" ),
  • ">"
  • );
  •  
  • // Return cleaned selector.
  • return( strSelector );
  • }
  •  
  •  
  • // I parse the selector into selectors classes.
  • SelectorTester.prototype.ParseSelector = function( strSelector ){
  • var arrSelectors = [];
  •  
  • // Clean and break the selector into sub-selectors.
  • var arrSubSelectors = this.GetSubSelectors(
  • this.CleanSelector( strSelector )
  • );
  •  
  • // For each sub-selector, we want to create a selector.
  • $.each(
  • arrSubSelectors,
  • function( intI, strSelector ){
  • arrSelectors.push(
  • new Selector( strSelector )
  • );
  • }
  • );
  •  
  • // Return the collection of selectors.
  • return( arrSelectors );
  • }
  •  
  •  
  • // I break up the original selector into sub-selectors.
  • SelectorTester.prototype.GetSubSelectors = function( strSelector ){
  • var arrSubSelectors = this.OriginalSelector.match(
  • new RegExp(
  • "[^\\[,]+(\\[[^\\]]+\\][^\\[,]*)*",
  • "gi"
  • )
  • );
  •  
  • // Loop over each to trim.
  • return(
  • $.map(
  • arrSubSelectors,
  • function( strSelector, intI ){
  • return( $.trim( strSelector ) );
  • }
  • )
  • );
  • }
  •  
  •  
  • // I reset the selector tester.
  • SelectorTester.prototype.Reset = function(){
  • // Clear any existing testing timeout.
  • clearTimeout( this.Timeout )
  •  
  • // Clear the original selector.
  • this.OriginalSelector = "";
  •  
  • // Destroy each selector.
  • $.each(
  • this.Selectors,
  • function( intI, objSelector ){
  • objSelector.Destroy();
  • }
  • );
  •  
  • // Clear selectors.
  • this.Selectors = [];
  • }
  •  
  •  
  • // I run the tests.
  • SelectorTester.prototype.Test = function( strSelector ){
  • var objSelf = this;
  •  
  • // Reset any existing test.
  • this.Reset();
  •  
  • // Store the selector and parse it.
  • this.OriginalSelector = this.CleanSelector( strSelector );
  • this.Selectors = this.ParseSelector( this.OriginalSelector );
  •  
  •  
  • // Define a method that will test each intermediary
  • // collection of nodes.
  • function PerformTest(){
  • var blnKeepTesting = false;
  •  
  • // Loop over each tester.
  • for (var i = 0 ; i < objSelf.Selectors.length ; i++){
  •  
  • blnKeepTesting = (
  • objSelf.Selectors[ i ].ShowNextSelector() ||
  • blnKeepTesting
  • );
  •  
  • }
  •  
  • // Check to see if we should perform another test.
  • if (blnKeepTesting){
  •  
  • // Launch test again shortly.
  • objSelf.Timeout = setTimeout(
  • PerformTest,
  • objSelf.Pause
  • );
  •  
  • }
  • }
  •  
  • // Start test harness.
  • PerformTest();
  • }
  •  
  •  
  • // -------------------------------------------------------- //
  • // -------------------------------------------------------- //
  •  
  •  
  • // I am a class for individual testing.
  • function Selector( strSelector ){
  • this.OriginalSelector = strSelector;
  • this.Parts = this.ParseSelector( strSelector );
  • this.Colors = [ "#F0F0F0", "#D0D0D0", "#B0B0B0", "#909090", "#707070", "#505050" ];
  • this.Depth = 0;
  • this.Nodes = $( "body" );
  • this.ResetNodes = $( [] );
  • }
  •  
  •  
  • // I parse the selectir into parts.
  • Selector.prototype.ParseSelector = function( strSelector ){
  • return(
  • strSelector.match(
  • new RegExp(
  • "[^\\s\\[]+(\\[[^\\]]+\\][^\\s\\[]*)*",
  • "gi"
  • )
  • )
  • );
  • }
  •  
  •  
  • // I show the next selector.
  • Selector.prototype.ShowNextSelector = function(){
  • if (this.Depth >= this.Parts.length){
  • return( false );
  • }
  •  
  • // Get the next set of nodes and colorize them.
  • this.Nodes = this.Nodes
  • .find( this.Parts[ this.Depth ] )
  • .css( "background-color", this.Colors[ this.Depth ] )
  • ;
  •  
  • // Build up the reset nodes collection.
  • this.ResetNodes = this.ResetNodes.add( this.Nodes );
  •  
  • // Increase the depth.
  • this.Depth++;
  •  
  • // Check to see if we have any more nodes to search.
  • // If not, we want to highlight this ndoe in gold
  • // to signify its a final node.
  • if (this.Depth >= this.Parts.length){
  • this.Nodes.css( "background-color", "gold" )
  • }
  •  
  • // Return true/false for next selectors.
  • return( this.Depth < this.Parts.length );
  • }
  •  
  •  
  • // I reset the select (take away background coloring).
  • Selector.prototype.Destroy = function(){
  • // Clear node colors.
  • this.ResetNodes.css( "background-color", "transparent" );
  • }

You Might Also Be Interested In:



Reader Comments

Jul 16, 2009 at 10:16 PM // reply »
10 Comments

I find using the Firebug console immensely useful for writing and debugging jQuery selectors.


Jul 17, 2009 at 7:50 AM // reply »
10,638 Comments

@Shayne,

Yeah, FireBug is the bomb!!


Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
InVision App - Prototyping Made Beautiful With Prototyping Tools Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
Feb 3, 2012 at 10:49 PM
How I Got Node.js Running On A Linux Micro Instance Using Amazon EC2
Wow this was really helpful! Only thing I would add is you need to update your .bash_profile after you edit the secure_path. This is what I did: $ . ~/.bash_profile Otherwise, NPM won't be found. ... read »
Feb 3, 2012 at 10:14 PM
Pushing Base64-Encoded Images Over HTML5 WebSockets With Pusher And ColdFusion
@Ben, Just wanted to let you know that pusher are soon to start limiting sizes on messages. This was the detail that came through in the Feb dispatch: "However, we will soon be limiting the s ... read »
Feb 3, 2012 at 5:05 PM
Regular Expressions Make CSV Parsing In ColdFusion So Much Easier (And Faster)
I tried using your RegEx in my C# program, but it was matching an extra empty-string at the end and so I would end up with an extra field that doesn't exist, so I changed it to this: (^|,)("(?: ... read »
Feb 3, 2012 at 3:47 PM
ColdFusion Supports HTTP Verbs PUT And DELETE (As Well As GET And POST)
Josh Cyr posted this on Twitter just a little bit ago. Thought it was appropriate. http://stackoverflow.com/questions/1619152/how-to-create-rest-urls-without-verbs/1619677#1619677 ... read »
Feb 3, 2012 at 2:28 PM
Changing The Execution Context Of Your Self-Executing Function Blocks In JavaScript
@Michael, You definitely make a good point (and extra points for quoting movies - I love movies). When you use a return() statement to define the object's public API, it does provide a consistent a ... read »
Feb 3, 2012 at 2:04 PM
Changing The Execution Context Of Your Self-Executing Function Blocks In JavaScript
To quote Jurassic Park: "Just because you can doesn't mean you should". I completely, utterly disagree with the thought that this is more readable. Consider the current module pattern: if ... read »
Feb 3, 2012 at 1:10 PM
REST API Design Rulebook By Mark Masse
@Jordan, Yeah, WRML was created by Mark Masse (author of the book). I also found it to be a bit convoluted. I suppose it is intended to allow the Client to be able to programmaticaly respond to cha ... read »
Feb 3, 2012 at 1:08 PM
ColdFusion Supports HTTP Verbs PUT And DELETE (As Well As GET And POST)
@Jason, To be honest, I don't have good answers for that kinds of stuff. And, to the point, that is specifically why I *really* liked the REST API Design Rulebook by Mark Masse - he just cuts throu ... read »