Debugging Javascript

Javascript has an incredibly powerful suite of debugging tools available, but you won’t find them in an IDE. Rather, Javascript’s debugging tools exist where your code is (typically) run - right in the browser.

With the increase of Javascript’s prominence in today’s web application oriented architecture, browser vendors have been quick to respond with robust debugging tools. The earliest incarnation of this was Firebug for Firefox. Since then, browsers like Safari and Chrome added their own tools, and they have become increasingly more powerful. Even Microsoft added Developer Tools with IE9 (although it does tend to lack more of the advanced features included with other browsers).

Open your Inspector

I’m going to call everything, from here on out, the inspector. Each browser has a different name, in Chrome its called the Web Inspector, Safari calls it the “Safari Developer Tools”, Firefox has Firebug, and even Internet Explorer has a kind of “Developer Tools”. The majority of these techniques, as one would expect, are found on Chrome, Safari and Firefox, but Internet Explorer 9 at the moment has only just started to catch up after being dormant on the inspector front for, well, forever.

The inspector is usually opened by right-clicking on a “blank” area of the screen and selecting Inspect Element or something along those lines based on your browser. In IE, press F12.

Chrome

Firefox

Safari

Internet Explorer

Using the console

One of the most fundamental tools to debug your Javascript is through the console. The console is the hammer to the inspector’s toolkit.

The console is essentially an environment to run your Javascript. While your first experience with it may be basic, you’ll soon find it is absolutely invaluable. With it, and depending where you are in the code, you can view the values of elements as your code is being run, make changes, and most importantly, test sample code.

If you get only a single takeaway throughout this entire article, let this discussion on the console be it. You can run any and all Javascript through the console; it is the foundation to Javascript debugging.

Let’s take a look at something we can do in console right here on this site. This site uses jQuery, so I’m going to run a through commands and we can see the results:

I’ve run some basic commands to demonstrate what the console is about: running Javascript. Anything you would put into your Javascript file you can put into the console, even multi-line items (use option/alt + enter to make a new line). It’s excellent for testing selectors, normal Javascript, or even trigging events. For example, let’s pretend you had a custom event listern on your document, if you simply wanted to fire it, you could enter $(document).trigger('customevent') within the console.

Using debugger

While the console offers some powerful tools, creating breakpoints through the use of the debugger statement actually turns your browser into a powerful IDE for Javascript debugging. Let’s take a look into how that works and how to leverage the (many) available features. We’ll start by making a basic HTML page which will include our Javascript.

index.html
1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html>
<head>
  <title>Debugging Javascript</title>
  <script src="scripts.js"></script>
</head>

<body>

</body>
</html>
scripts.js
1
2
3
4
var foo = 'a';
debugger;
foo = 'b';
debugger;

Open index.html in your browser. Initially, you should notice if you don’t have your web inspector open that nothing happened. Most inspectors will ignore things like debugger statments unless the inspector is open. However, if you open your inspector and hit Refresh on your browser, you likely will get something which appears like this:

Let’s take a look at our surroundings.

The browser is telling us the execution is paused. The page has stopped “working” until we tell it to continue on. Under the Sources panel we can see which line of code we’ve paused on, which, when using debugger, is always on the debugger line itself. While this alone is partially interesting, there’s a lot more power to extract here. We want to use the console and play around. While you could switch to the console tab itself, wouldn’t it be better to have the console right here? Press Esc to get the console to appear.

Now we’re cooking with fire! With the console we can do anything to the current state. We could rewrite foo, delete it, create a new variable, inspect other variables, etc. For now, let’s take a look at the value of foo.

Viewing the value of variables mid-way through execution is an incredibly powerful for debugging. As mentioned earlier, we can anything we normally could in Javascript here. Let’s change foo’s value for fun and profit:

Here we set foo to c. Not exactly mind blowing, but we’re just getting our feet wet. But what if we’re done fooling around and want the rest of the code to execute? For that, we’ll want to take a look at the debugging tools.

  1. Resume script execution Resumes your script if it has been paused due to a debugger statement or breakpoint.
  2. Step over next function call Steps to the next line of code, attempting to remain in the same current scope if possible.
  3. Step into next function call Dive into the next function we encounter. Where as the previous button will attempt to remain in the same scope, this function will bring us into the next function we encounter.
  4. Step out of the current function If you’re inspecting a function you’re not concerned about, this will escape you out to its parent scope.
  5. Deactivate all breakpoints Removes all breakpoints, though not debugger statements. Especially useful once you’re finished debugging and want to get on with your life.

In this case, let’s hit Resume script execution to move on. Because we have two debugger statements in our code, we expect to get halted again on the second statement.

This time around we see that our foo = 'b' code has been executed, and if we inspect foo we see it is currently set to b.

You can either press the Resume Script Execution button or close the web inspector to have the page finish out.

Breakpoints

Similar to the debugger statement, the inspector supports breakpoints. Breakpoints have a few advantages:

  1. They do not pollute your code with dangerous debugger statements which could accidentially be committed.
  2. They support conditional breaking.
  3. They allow you to debug code that perhaps isn’t even yours to edit.

However, breakpoints have one minor disadvantage: you need to find the line to break on using the Source panel, which can sometimes be inconvenient and counterintuitive, especially if you’re already editing the code in question with your editor.

There is no right or wrong way in this case, they’re two sides of the same coin. It’s best to understand both methods.

The setup

Let’s edit our scripts.js file to make it look like this:

scripts.js
1
2
3
4
5
6
7
8
var
  i,
  length,
  names = ['Bill', 'Frank', 'James', 'John'];

for(i = 0, length = names.length; i <= length; i++) {
  console.log(names[i]);
}

With your inspector open, open index.html again and let’s take a look at the console.

This looks fantastic, but we’re getting that nasty undefined output. Why is that? Well, let’s start by putting in a breakpoint.

Creating a breakpoint

Under the Sources tab, click the line number where we want to create a breakpoint (breakpoints are essentially identical to debugger statements). You should see something identical to the image below:

Refresh the page and let’s see what happens.

We can see that our execution has paused on our breakpoint. We can play around in the console as well (remember to bring it up when you’re under the Sources tab by pressing Esc on your keyboard). If you test our i value we can see it is currently o, and names[i] is properly set to Bill.

We can continue the script by pressing the Pause script execution button, which will be triggered each iteration throughout the loop. You’ll notice that the script outputs John, then goes on one more time. There’s our problem!

Conditional Breakpoints

However, if we knew the problem was with the value being undefined, wouldn’t it have been better to only break when names[i] was undefined? It would have sure saved us a lot of clicking, and imagine if this was a much larger array!

Create a conditional breakpoint by right-clicking the breakpoint and selecting Edit Breakpoint…. You will be presented with this dialog:

Here we can enter in whatever condition that needs to evaluate to true for this breakpoint to be fired, and here we have the single biggest advantage breakpoints have over debugger statments.

Enter your condition like so and press Enter:

A conditional breakpoint appears as an orange flag:

Note: I sometimes find the browser can be extremely flakey in accepting the statement. If you are finding the orange flag denoting the breaking is a conditional breakpoint is not appearing, try to remove the breakpoint entirely and recreate it. Don’t click “outside” the dialog box to confirm - that never seems to work. Always press enter.

Refresh the page again and let’s see what happens.

The script has run, and we can see in the console the breakpoint has been skipped for all counts with the exception of when our statement returned true, when our names[i] was undefined. That’s a lot cleaner and easier to debug!

Examining the interface

Of course the main script area and console are not the only visible items on the Sources tab. There’s also the area off to the right which contains some extremely powerful tools.

I guarantee if you’re not using these at the moment, they’re about to fundamentally alter the techniques you will use to debug.

Watch Expressions

Create a watch expression when you find yourself debugging and constantly wanting to know the value of a variable. Using the same code above, you could add a names[i] watch expression and, by setting a normal breakpoint on the console log, watch each value as it ticks by (useful if you were not already logging the value to the console.)

Adding the expression:

Refresh the page with the breakpoint set to easily see the content of the variable:

TL;DR Use watch expressions when you find yourself investigating the same variable over and over again while debugging.

Call Stack

The call stack will be very familiar to anyone who has worked in compiled languages equipped with IDEs. The call stack is essentially the functions or methods the browser has executed to get to the current line of code.

The call stack answers the question of, “How did this line of code get executed?” Even better, you can hop back through to the parent function(s) and view their current state(s) as well.

Let’s set up an example:

scripts.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var
  i,
  length,
  names = ['Peter', 'Bruce', 'Tony', 'Clark', 'Hal'],
  lastNames = ['Parker', 'Kent', 'Stark', 'Wayne', 'Jordan'];

function concatenateName(firstName, lastName) {
  return firstName + ' ' + lastName;
}

function processNames() {
  for(i = 0, length = names.length; i < length; i++) {
      var random = Math.floor(Math.random() * lastNames.length);
      var name = concatenateName(names[i], lastNames[random]);
      console.log(name);
  }
}

processNames();

This script will output first names with random last names to our console. Nothing special, but it gives us a tiny example to play with.

Let’s start by putting a breakpoint on line 8.

Refresh the page and look at the call stack item to the right.

What we have here is a list of functions we had to run through to get to this item, the top most being the most recently run function. In this case, we find ourselves in the function concatenateName from line 8 of scripts.js, which was called by processNames from scripts.js line 14, which itself was called by an anonymous function (in this case, nothing) in scripts.js on line 19.

First, this gives us an immediate understanding of the order of execution and what happened to get us to where we are. This alone is invaluable, but there’s more.

If you actually click on one of the other stacks, in this case processNames there in the Call Stack window, your scope will be brought to that point in time. You can actually inspect and view which variables are currently set.

This tool can be immensely powerful for debugging complicated Javascript applications and is fundamental in compiled languages. One additional nice benefit is by traversing the various items within the call stack, is the scope variable window will be automatically updated.

Scope Variables

The scope variable window shows exactly what variables exist within the current scope. While it doesn’t add any additional functionality, it can be nice to simply glance over at the scope variable window to quickly view the value of a variable.

Using the previous example with execution being paused at line 8, we can quickly see the firstName is set to Peter and lastName is being set to Jordan.

DOM Breakpoints

DOM breakpoints are a powerful tool if you’re not sure what piece of code is modifying an element. Let’s set up an example:

index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html>
<head>
  <title>Debugging Javascript</title>
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
  <script src="scripts.js"></script>
</head>

<body>

  <div class="content">
      <p>Loading...</p>
  </div>
  <button name="toggle">Load Content</button>

</body>
</html>
scripts.js
1
2
3
4
5
$(document).ready(function() {
  $("[name=toggle]").on("click", function() {
      $(".content").html('<p>Finished loading</p>');
  });
});

Next, we’ll create our DOM breakpoint. To do this, find the container element which encapsulates the element(s) you’d like to monitor for changes. In our example, since our code is going to remove the <p>Loading...</p> item, we’ll want to assign the DOM breakpoint to the .content element by specifying we want to break on Subtree Modifications:

Next, click on the Load Content button, and let’s see what happens:

Script execution has been paused and the browser will display the Sources tab. The most interesting thing to us, at this point, is the call stack. We can see from the call stack there are a number of jQuery functions which are being run to begin DOM modification. But what line of ours actually triggered the change?

Looking at the call stack, we can see there is a reference to scripts.js on line 3. Click on that.

Here’s the line that we wrote which is triggering the call to modify that DOM element! This can be a highly useful tool if you’re unsure what line of code is making modifications to your DOM.

Conclusion

With these tools in your toolbelt, your Javascript debugging experience should be significantly easier and more enjoyable. Personally, I love sniping out issues in code and addressing issues knowing exactly what is going on without the need for modifying the existing code, and I hope now you will too!

Good (bug) hunting!

Comments