How to Debug Your jQuery Code

Elijah Manor | November 23, 2009

In the past 10 years, more and more Web development has moved off the server and migrated to the browser, making a user's experience more responsive. Unfortunately, this transition hasn't been very pleasant for developers, given that any attempt to debug client-side code eventually means resorting to the classic JavaScript alert methods, which we are all accustomed to seeing in code such as the following:

<script type="text/javascript"> 
   alert('Alerts are so NOT the Bee\'s knees.\n\n' + 
      'To the contrary, they are the 3 Ds... \n' + 
      '• Difficult to format\n' +  
      '• Disrupt the timing of JavaScript events\n' + 
      '• Disagreeable to the Nth degree!\n\n' + 
      'In summary, stay away from the alert statement as a debugging tool.'); 
</script>

(You can view, run, and edit the source code for this example at jsbin.com/opayo/edit. I’ve provided pointers like these for each of the other examples in this article as well.)

Not only are the JavaScript alert methods annoying, but until recently developers haven't had any really good tools to help them debug client-side code when they’re trying to re-create or resolve errors. Alert methods can also result in certain false assumptions about your code. For example, an alert dialog box waits to be closed before execution proceeds, so the timing of events can be off if you use this tool as a debugging mechanism.

In this article, I’ll describe some techniques to assist  you in the debugging process when you are using jQuery in client-side Web development. Thankfully, we have much better tools and technologies than developers had even five years ago.

Getting Started

For me, the best way to work with jQuery is to develop first on Firefox and then test my code on Internet Explorer. There are other browsers, of course, but if you take this approach, more often than not other browsers fall in line with little to no additional effort.

Apps have also become a little harder to develop and debug in light of the concept of unobtrusive JavaScript . With unobtrusive JavaScript, instead of tightly binding event code to a DOM element, you bind events after the DOM is rendered, allowing for graceful degradation of the code if JavaScript happens to be turned off. Look at the following example (jsbin.com/otowo/edit) for a simple illustration of the point.

<script type="text/javascript"> 
$(function() { 
   $('#helloUnobtrusive').bind('click', function() { 
      $(this).text('Hello Unobtrusive Elijah!'); 
   }); 
}); 
</script> 

<p id="helloObtrusive" 
   onclick="$(this).text('Hello Obtrusive Elijah!'); return false;">
   Hello Obtrusive JavaScript!
</p> 
<p id="helloUnobtrusive">Hello Unobtrusive JavaScript!</p> 
<a href="https://jsbin.com/otowo/edit" target="_blank">
   View, Run, &amp; Edit Code
</a>

Given that most browsers have JavaScript turned on, I think the value of unobtrusive code is that it separates the layout of the DOM from the manipulation of the DOM. However, this separation also means that your code can become a little more difficult to debug.

Necessary Tools

Most of the tools that I’ll demonstrate are specifically for Firefox. Internet Explorer 8 actually has some great built-in tools that you can use in addition to those that I highlight, but I tend to use Internet Explorer’s tools only if an issue comes up after I have done my initial Firefox development. Here are some brief descriptions of the tools used in this article. I’ll review them more in detail in the following sections.

  • Firebug—Although many developers have used Firebug, I don't think that many understand the true potential of its debugging capabilities. If you know your tools inside out, you can become a more productive and efficient programmer. Note: If you aren't using Firefox, the Firebug Lite bookmarklet can come in handy to provide many of the nice debugging features of its Firefox add-on counterpart. I tend to use this bookmarklet primarily with Google Chrome.
  • FireQuery—This Firefox Firebug add-on provides some nice features to Firebug, such as letting you see which jQuery events are attached to what DOM elements, displaying any data elements that are attached to a DOM element, and providing the ability to inject jQuery into Web pages that don't have it already loaded.
  • FireFinder—This Firefox Firebug add-on can help you find the selector you are looking for. You can test the selector you have, and it will highlight all those items on your Web page.

Debugging Selectors

The starting point of any jQuery development is getting the right selector. From there, you can attach events, perform animations, and so on. Sometimes, however, getting the exact selector you need is hard. Thankfully, several tools and plug-ins are available to help you find the selector that’s right for you.

Firebug Console

The first place to start is probably the Firebug console to test your selector. You can test your selector by typing it in the Firebug console window and then clicking Run, as shown in Figure 1 (jsbin.com/edito/edit).

Figure 1: Firebug console shows a successful match from a jQuery selector
Figure 1: Firebug console shows a successful match from a jQuery selector

If your selector matches one or more DOM elements, the elements will show up in the console window as "jQuery ( a#selectMe elijahmanor.com)" or as whatever your particular selection was. If nothing is selected, you will see something that looks like Figure 2 (jsbin.com/edito/edit).

Figure 2: You see no results if your selector has no match
Figure 2: You see no results if your selector has no match

FireFinder

If you have problems figuring out why your selector is not returning the results you want, you can use some other options to help you out. One of them is the FireFinder Firefox Firebug add-on. You type in your selector, and then the add-on highlights all the DOM elements on the page that match what you entered. Figure 3 shows an example (jsbin.com/edito/edit).

Figure 3: Using FireFinder to highlight elements returned from your jQuery selector
Figure 3: Using FireFinder to highlight elements returned from your jQuery selector

If at first you don't get the result you expect, you can try to back off your selector to something more generic and then progressively get more specific in your selector. Or you may just want to type the selector you think should work and then slowly tweak it until you get the result you want.

In Figure 4 (jsbin.com/unadu/edit), I start with the selector "div a[href$='.zip'][title*='Elijah']". The goal is to find all the links referencing zip files with a title that starts with "Elijah". First let's try out the selector in FireFinder to see what is being selected.

Figure 4: Use FireFinder to debug a faulty jQuery selector
Figure 4: Use FireFinder to debug a faulty jQuery selector

It appears that one too many selections are being made. The elijah5.zip link should not be selected because the title attribute doesn't start with "Elijah". A little research on jQuery's Web site shows that we used the wrong attribute to test for “starts with”. We should have used the following selector: "div a[href$='.zip'][title^='Elijah']". You can try the updated selector in FireFinder, and it will highlight only the elements that we want.

jQuery Trace Plug-in

Another tool I ran across recently to help in debugging selectors is the jQuery Trace Plug-in. This plug-in takes apart a complex selector (from left to right), finds and highlights the most general selection, and then progressively narrows the focus, applying darker and darker highlighting to the DOM elements until the final selection is highlighted in gold. This is a unique approach to seeing the path of your selector. The hope is that if your selector is incorrect, you can watch the time-lapsed animation to easily pinpoint where the selector has gone wrong.

The following code (see jsbin.com/unadu/edit), a slightly modified version of the code shown earlier, helps demonstrate this unique plug-in.

<script type="text/javascript"> 
$(function() { 
   $('#traceMe').click(function() { 
      console.group("Trace"); 
      $.trace("div p a[href$='.zip'][title^='Elijah']"); 
      console.groupEnd(); 
   }); 
}); 
</script>

The code traces the selector "div p a[href$='.zip'][title^='Elijah']", which first selects all the div elements on the page and applies a light gray background. It then selects all paragraph tags inside of divs and highlights them with a slightly darker shade of gray, and then finally selects all the anchors within those divs with the specified attributes and shades them in gold (which represents the goal of the selection). Figure 5 illustrates the results (jsbin.com/apepa).

Figure 5: Using a jQuery plug-in to trace your selector path
Figure 5: Using a jQuery plug-in to trace your selector path

Debugging Events

Let's say you’re having issues with your event handlers not firing or that they present some issue during their executions. How do you go about debugging such a scenario? Well, there are several techniques you can try, and I’ll examine some of them in the following examples.

Check Whether an Event Is Registered

A common problem you might have is that your code isn't running because you either attached to the wrong event or you added an event handler but to the wrong element. Depending on how you registered the event, it will show up in different places. Let's start by using the Firebug command line and then look at some other options.

Use the Firebug Command Line

The first step you might take is to find out whether your event handler is registered with the event that you expected. If you used either the bind method or one of the jQuery event helpers (click, hover, keypress, and so on) to register your event handler, you can see which events are attached to the actual DOM element by using the jQuery data method.

The following code snippet (see jsbin.com/asili/edit), modified from Cody Lindsey's jQuery Enlightenment eBook, displays all the events attached to a particular DOM element using the Firebug console.

<script type="text/javascript"> 
$(function() { 
   $('p').bind('click mouseleave', function(event){ 
      console.log(event.type);        
   }); 
 
   //Open Firebug and look at the console to see the attached events 
   console.dir($('p').data('events')); 
}); 
</script> 

<p>Hover over this to reveal attached events...</p>

First, the click and mouseleave events are attached to the paragraph tag. The console.dir Firebug method is then used to get the event information stored in the paragraph DOM element and display it in the console window. Take a look at Figure 6 (jsbin.com/asili) for the results of the executed code. You might also notice that when you click or mouseover the paragraph, the event name is displayed in the console.

Figure 6: Firebug console showing the events that are attached to the DOM element
Figure 6: Firebug console showing the events that are attached to the DOM element

If you used the live method to attach your event handlers, the approach I just described does not work because the event handler information hasn't been stored in the DOM element using the jQuery data method. Instead, the information is stored at the top-level document DOM element. For more information about this, read the article by Neeraj Singh entitled How live method works in jQuery. Why it does not work in some cases. When to use livequery. This article is a great resource for understanding the difference between the bind and live methods, but you should keep in mind that it refers to the 1.3 release of jQuery. The 1.4 release of jQuery has live method support for all the events, and the livequery plug-in the article mentions is no longer necessary.

The following code (jsbin.com/acihi/edit) shows the difference in how the data method is stored with the bind and live jQuery methods.

<script type="text/javascript"> 
$(function() { 
   $('#helloWorld').bind('click', function() { 
      $(this).text('Hello Elijah!'); 
   }); 
   $('#goodbyeWorld').live('click', function() { 
      $(this).text('Goodbye Elijah!'); 
   });    
}); 
</script>

The output of this code is displayed in Figure 7 (jsbin.com/acihi/edit). I also copied, pasted, and executed the <pre> contents into the Firebug console command line. I used a combination of Firebug's group console method to separate a logical set of statements, and I also used Firebug's dir console method to display an interactive representation of an object's properties.

Figure 7: Bind event is attached to associated DOM element and live event is on document element
Figure 7: Bind event is attached to associated DOM element and live event is on document element

Although you can show that the live event is attached to the root document DOM element, you can't navigate to the event handler by clicking the green function link from the Firebug console, as you can when you use the bind method. If you try clicking the function link, it just tries to navigate into the guts of the jQuery library instead of the function you attached with the live method. Even though you can't navigate to the event handler, you still have a good idea of what’s going on because if you expand the live tree node in the Firebug console, it lists concatenated names of the selector and the event type that have been registered via the live method. The selector and event information is necessary for the live method so that it can respond to dynamically created elements in your DOM.

Use Firebug's FireQuery Plug-in

Instead of using the Firebug console and command line to look for your jQuery event handlers, you can use the FireQuery plug-in for Firebug. This plug-in displays on the HTML tab any events attached to the DOM element. In Figure 8 (jsbin.com/ufezi/edit), you can see on the HTML tab that the #helloWorld paragraph has a click event attached to it, as indicated in gray text next to the element.

Figure 8: FireQuery shows events and data attached to DOM elements
Figure 8: FireQuery shows events and data attached to DOM elements

Not only can you visualize events, but you can also see other information that might be attached to a DOM element using the jQuery data method. You might notice that the "Hello Goodbye" paragraph has some additional information that is visible on the HTML tab of Firebug. The data key "artist" was used to store "THE BEATLES" to the “#whoWroteThatSong” paragraph DOM element.

Use Firebug's Debugging Features

Assuming you know that your event has been registered with the correct element, the next step is to know whether it is running as you intended it. So many options are available in Firebug that it is hard to know where to start, but let's begin with some scenarios for what you need to know and then track down which feature can help get the job done.

Scenario 1: I just want to set a breakpoint and debug my JavaScript code and examine the flow and variable values.

There are several ways to start a debug session in your code in Firebug.

1. Set a breakpoint next to your line of code

As you can with most debuggers, you can set a breakpoint at the left of a code line number, as shown in Figure 9 (jsbin.com/ewavu/edit). When the breakpoint is reached, the debugger kicks in and pauses on the line you selected. At this point, you have access to all the normal debugging features you'd expect, such as control flow, stack traces, and watch expressions.

Figure 9: Setting a breakpoint next to your line of code
Figure 9: Setting a breakpoint next to your line of code

2. Set Firebug to automatically break on an error

Instead of setting a breakpoint where you think your bug is happening, you can just set the Enable Break On All Errors feature in Firebug to start a debug session automatically when an error occurs. You can turn on this feature by clicking the Pause button in the top menu on the Console tab. Figure 10 shows an example (jsbin.com/aciro/edit)

Figure 10: Automatically breaking on error
Figure 10: Automatically breaking on error

3. Use the debugger keyword

A lesser known feature of Firebug is that you can use the debugger keyword inside your code to start a debug session at that line of code, as shown in the following example (jsbin.com/ugavi/edit). Execution stops at that line, and you can proceed with your debugging as you would normally. Obviously, you don’t want the debugger keyword in your production code, but it can be convenient to use during the development process.

<script type="text/javascript">
$(function() {
   $('button').click(function() {
      var firstName = 'Elijah';
      debugger;
      var lastName = 'Manor';
      var fullName = firstName + ' ' + lastName;
      console.log("Your name is %s. I hope you have a great day!", fullName);
   });
});
</script>

Scenario 2: One of my event handlers is being called, but I don't know which particular element is triggering the event.

The good news is that the this keyword is represented in the watch window and can indicate which element trigged the event that you are currently debugging. Figure 11 (jsbin.com/usumo/edit) shows that the p#helloWorld element caused the click event that is being debugged.

Figure 11: The this variable in the watch window shows you which element caused the event
Figure 11: The this variable in the watch window shows you which element caused the event

Scenario 3: I am getting an error deep inside a set of helper methods, and I'm not sure where the error originated.

Firebug maintains the stack trace for you, so you can track the flow of methods back to its origin. There are two main ways to accomplish this task.

1. Set a breakpoint and view the Stack tab

The easiest way to get a stack trace is to set a breakpoint somewhere in your code. Once the debugger pauses on the code, the information is available on the Stack tab in the pane next to the script window. Figure 12 shows an example (jsbin.com/eyaxe/edit).

Figure 12: The stack window reveals the execution path
Figure 12: The stack window reveals the execution path

 As you can see in Figure 12, the stack trace shows several levels of methods. The console.trace call was made within the firstName method, which was called from the fullName method, which was called from our anonymous event hander. From there, the stack trace dives into the jQuery library code.

2. Call the console.trace method inside your code

Instead of setting a breakpoint, you can figure out the stack trace by adding some code to your method. The following code snippet (jsbin.com/iduqu/edit) shows how I call the console.trace method from a method call that is several levels deep in the calling stack.

<script type="text/javascript">
$(function() {
   $('button').click(function() {
      console.group('Calling the fullName() method');
      console.log(fullName());
      console.groupEnd();
   });
});

function fullName() {
   return firstName() + ' ' + lastName();
}

function firstName() {
   console.group('Tracing firstName() method');
   console.trace();
   console.groupEnd();
   return 'Elijah';
}

function lastName() {
   return 'Manor';
}
</script>
<p><button>Click Me</button></p>

The output of the preceding code snippet is a stack trace that shows up in the Firebug console window. The output is the same as the one we saw earlier in Figure 12, but here it is produced programmatically instead of by using a breakpoint and the Stack window.

Scenario 4: A particular event handler appears to be taking a really long time to execute, and I need to determine where the bottle neck is and fix it.

Firebug provides several techniques to help you diagnose performance problems in your code.

1. Use the console.time methods

You can wrap your code with console.time and console.timeEnd to find the amount of time that passes between calls to these methods. This information can be helpful if you know that the code you are testing has some performance issues and you are curious about its impact. However, this approach does not break down the elapsed time into any methods that might have executed during that time period. If you need that kind of detail, you need to use the console.profile feature described in the next section. The following code (jsbin.com/aqibe/edit) shows an example of using console.time and console.timeEnd.

<script type="text/javascript"> 
$(function() { 
   $('button').click(function() { 
      console.time('Random Number Sum'); 
      var randomNumberSum = 0; 
      for (var i = 0; i < 100000; ++i) { 
         randomNumberSum += Math.floor(Math.random() * 11); 
      } 
      console.timeEnd('Random Number Sum'); 
      console.log('Random Number Sum: %d', randomNumberSum); 
   }); 
}); 
</script> 

<p><button>Click Me</button></p>

The output of the this code is shown in below. The 100,000 for loop adding random numbers from 1 to 10 took 9 milliseconds and resulted in a sum of 500,405.

Random Number Sum: 9ms
Random Number Sum: 500405

2. Use the console.profile method

If you need detailed statistics about your code, you should try the console.profile feature of Firebug. You can initiate the profile through the console window or through code. Lets demonstrate this by profiling the following code snippet (jsbin.com/ufijo/edit).

<script type="text/javascript"> 
$(function() { 
   $('button').click(function() {    
      var randomNumberSum = 0; 
      for (var i = 0; i < 100000; ++i) { 
         randomNumberSum += getRandomNumber(10); 
      } 
      console.log('Random Number Sum: %d', randomNumberSum); 
   }); 
   function getRandomNumber(maximumNumber) { 
      return Math.floor(Math.random() * getNumberPlusOne(maximumNumber)); 
   } 
   function getNumberPlusOne(number) { 
      return number + 1; 
   } 
}); 
</script> 

<p><button>Click Me</button></p>

First, you click the Profile button from the console window for Firebug to start collecting information. When you are ready to stop collecting information, click the Profile button again. At that point, Firebug displays statistics from the profile session in the console window, as you can see in Figure 13 (jsbin.com/ufijo/edit)

Figure 13: Result from a profile session revealing performance statistics for called functions
Figure 13: Result from a profile session revealing performance statistics for called functions

If you need more control over when your profile session starts and finishes, then there is another option for you. You can surround your code with console.profile and console.profileEnd and at the end of the profile, you will see the information that Firebug collected inside the console window. The output of the profile looks the same as the above manual approach in Figure 13 (jsbin.com/oqiwa/edit).

<script type="text/javascript"> 
$(function() { 
   $('button').click(function() { 
      console.profile();       
      var randomNumberSum = 0; 
      for (var i = 0; i < 100000; ++i) { 
         randomNumberSum += getRandomNumber(10); 
      } 
      console.profileEnd(); 
      console.log('Random Number Sum: %d', randomNumberSum); 
   }); 
   function getRandomNumber(maximumNumber) { 
      return Math.floor(Math.random() * getNumberPlusOne(maximumNumber)); 
   } 
   function getNumberPlusOne(number) { 
      return number + 1; 
   } 
}); 
</script> 

<p><button>Click Me</button></p>

Scenario 5: You are getting JSON back from an AJAX call, but you finding it difficult to make sense of it

Something I did for a while was copy and paste the JSON returned from an AJAX call and throw it into an online JSON formatter to quickly see whether all the pieces I needed matched up with what I expected. Then I realized that Firebug actually does that for me! You can expand an AJAX call inside of the Firebug console window and it will includes a JSON tab that is parsed and formatted in a tree like fashion. You can seen an example of this in Figure 14

Figure 14: JSON results from AJAX call are parsed and displayed in special Firebug tab
Figure 14: JSON results from AJAX call are parsed and displayed in special Firebug tab

Debugging Chains

If you have done very much jQuery development at all, you've probably seen examples and used the concept of chaining together your jQuery methods. Not only is this a best practice for performance, but it also leads to clean, simple-looking code.

In spite of the many benefits chaining gives you, it also makes it difficult to debug values anywhere in the middle of a long chain. For example, it would be quite difficult to determine the status of the jQuery this object in the middle of a long chain of actions. Instead of converting a long chain into separate statements, you can instead use a very small jQuery Log Plug-in originally written by Dominic Mitchell. The following code (jsbin.com/opaya3/edit) shows the actual plug-in, and the second sample shows it in action.

jQuery.fn.log = function (msg) {
   console.log("%s: %o", msg, this);
   return this;
};
<script type="text/javascript">
$(function() {
   $('p').log('BEGIN')
      .html('<ul><li>Dynamic Text</li></ul>')
      .log('After Add Dynamic Text')
      .find('li')
      .log('After Find LI')
      .animate({
         fontSize: "24px",
         marginLeft: "15px"
      }, 1500)
      .log('After Animate')
      .animate({
         fontSize: "16px",
         marginLeft: "0px"      
      }, 1500)
      .end()
      .log('Reset back to P tag')
      .log('END');
});
jQuery.fn.log = function (msg) {
   console.log("%s: %o", msg, this);
   return this;
};
</script>
<p>Static Text</p>

The output of this code snippet is shown in Figure 15 (jsbin.com/opaya3/edit). As you can see, you can call the log method throughout the chain at different levels, and it will print a log message along with the selectable jQuery object.

Figure 15: Output from the jQuery log plug-in used in a long chained jQuery statement
Figure 15: Output from the jQuery log plug-in used in a long chained jQuery statement

This plug-in could be enhanced by adding code to support browsers that don't recognize the console.log method (such as Opera and Internet Explorer). It would also be nice to have a setting that disables logging completely at a global level.

Using Conditional Breakpoints

Most of you have probably used a debug session in Firebug to track down your bugs in jQuery, but what about when you need a breakpoint in a method that is called a whole bunch of times and all you need is to debug it in one particular case? It can be very frustrating to continually hit breakpoint after breakpoint looking for the one scenario you need to debug.

Firebug has the concept of conditional breakpoints, for which you can provide a Boolean condition that must evaluate to true before it actually stops execution and launches the debugger. If you haven't used this feature before, it can save you lots of time. All you have to do is right-click the breakpoint next to the line number and provide and set your conditional statement. Figure 16 (jsbin.com/ukoru/edit) illustrates an example of setting a conditional breakpoint from the previous console.time code snippet. The statement tells the debugger to pause when the loop index equals 32.

Figure 16: Setting a conditional breakpoint in Firebug
Figure 16: Setting a conditional breakpoint in Firebug

Wrapping Up

With the advent of new Web development techniques that move code closer to its interaction with the user, developers need to change their debugging practices so that they can quickly and efficiently develop and resolve issues.

I hope the tips and tricks I’ve described here are useful in your current or next project. There are so many great tools now available and so many features that many developers don't even know about. I especially like Firebug, and I suggest that you get to know the tool and know it well.

If you are interested in continuing your jQuery learning, I encourage you to follow me on Twitter for a fresh set of jQuery links and to check out my blog for my daily Tech Tweets roundup that contains numerous jQuery links to aid in your learning process.

 

About the Author

Elijah Manor is a Christian and a family man. He develops at appendTo as a Senior Architect providing corporate jQuery support, training, and consulting. He is an ASP.NET  MVP, ASPInsider, and specializes in ASP.NET MVC and jQuery development. He enjoys blogging about the things he learns. He is also active on Twitter and provides daily up-to-date Tech Tweets.

Find Elijah on: