Tragically, debugging is a skill only really learned through practice, but the good news is that you’ll get lots of practice! To be completely honest, JavaScript can be a challenge to debug, more so than other languages in my experience, largely due to those pesky browsers. But there are definitely tricks to be learned, the most important of which are presented in this chapter, along with some of the basics of error types and causes.
In Chapter 12, you’ll learn how to handle the errors that do arise in a grace-ful manner.
error tYPes
Three general types of errors may occur:
J Syntactical
J Run-time
J Logical
Syntactical errors are caused by improper syntax and prevent JavaScript from running at all. For example, failing to balance all quotation marks, parentheses, and curly brackets will have this effect. Syntactical errors can be minimized by using a text editor or IDE that provides syntax highlighting and character balancing. The good news about syntactical errors is that they’re generally easy to find and fix.
Just be certain to watch your browser’s error console (Figure 3.16) so you’re made aware of syntactical errors when they occur. The bad news about syntactical errors is that the error message won’t necessarily accurately represent the problem. For example, Figure 3.16 says there’s a “missing ; before statement,” but the actual problem is that the keyword var was entered as just ar.
fiGURe 3 .16 A syntactical error shown in Firebug’s Console panel.
NOTE: if your Javascript code doesn’t seem to execute at all, it could be because of a syntactical error.
ptg7799847 Run-time errors are those that occur while the JavaScript code is being executed.
Examples include referencing objects or functions that don’t exist. Again, the browser’s error console will report such problems. Many browser-specific issues (e.g., varied support for specific features) qualify as run-time errors.
Logical errors aren’t true errors in the sense that the browser or IDE will report a problem, but occur when the result of some code isn’t what you expect it to be.
In a word, logical errors are bugs, commonly caused by the code doing exactly what you told it to, meaning that the source of the mistake can be found between the keyboard and your chair! Fortunately, applying some best practices—covered in this book—will help to prevent logical errors. When they do occur, and they inevitably will, applying the debugging techniques outlined in a couple of pages should help you squash the bug.
CoMMon error Causes
The causes of many common errors won’t mean much to you yet, as you haven’t been formally taught most of the language (acknowledging that you’ve probably played with JavaScript some). Still, there are a few things you should know to watch out for:
J Variable names
Variable names in JavaScript are case-sensitive, meaning that myVar and myvar are two different things. Find a consistent naming scheme (to be discussed in the next chapter) and stick to it!
J Function names
Function names are also case-sensitive, whether you’re the one who has defined the function or not (i.e., the function is predefined for you).
J Object names
Object names are, yes, also case-sensitive. When using, say, the Math object in Chapter 4, Simple Variable Types, you must write Math, not math or MATH.
NOTE: Javascript is a case-sensitive language!
errorS and deBugging 81
ptg7799847
J An imbalance of quotation marks, parentheses, angle brackets, or curly braces
As I just stated, an imbalance of quotation marks, parentheses, angle brack-ets, or curly braces all lead to syntactical errors. Having a good text editor or IDE can go a long way toward ensuring there’s a closing character for each opening one.
J Mistakenly using = instead of ==
In the next chapter, you’ll formally learn that a single equals sign (=) is the assignment operator, and in Chapter 5, Using Control Structures, you’ll see that a double equals sign (==) is the equality operator. The first assigns a value to a variable; the second tests if two values are equal. Using a single equals sign when you should use two leads to logical errors.
J Referencing objects that don’t yet exist
Explained in Chapter 2, this can happen if JavaScript attempts to access DOM elements before the DOM has been fully loaded (among other reasons).
J Treating an object of one type as if it were another type
This will mean more in time, but you’ll sometimes get errors—both run-time and logical—if you treat, for example, a non-string as a string or a non-number as a number.
J Using a reserved word
There are a couple dozen reserved words in JavaScript: var, function, and so forth. You cannot use one of those reserved words as the name of your variable or function. That being said, I’ve never been inclined to include the list of reserved words in a book: many resources online will do that for you and the list is too long to memorize regardless. But if you use descriptive names for the variables and functions you create, you’re unlikely to conflict with a reserved word, which are more generic by design.
ptg7799847 deBugging teChniques
With an understanding of the fundamental error types and common causes, let’s look at some debugging techniques.
J Get a good text editor or IDE.
Not to belabor the point, but choosing and mastering a good text editor or IDE will make your JavaScript life much, much easier. That’s its raison d’etre, after all!
J Get a good development browser.
This topic was also discussed earlier in the chapter: choose a good browser with the right extensions (when applicable) and learn how to make the most of it.
J Keep the browser’s console open at all times.
For better or for worse, browsers don’t make a big fuss when things go wrong, meaning there can be problems you’re unaware of. By keeping the browser’s error console visible, you’ll see the problems that occur.
J Use a JavaScript validator.
Just as there are HTML validation services, there are JavaScript validation services. One such site is JSLint (www.jslint.com), created by Douglas Crock-ford, a JavaScript master. JSLint is a “code quality tool” that identifies both problematic and potentially problematic code.
A more pleasant alternative is JSHint (www.jshint.com), derived from JSLint.
The argument against using JSLint is that it’s rather conservative and strict, advocating for doing things pretty much how Crockford thinks you should.
JSHint serves the same purpose, but can be customized to be flexible as to what is or is not considered to be a code quality issue.
errorS and deBugging 83
ptg7799847
J Use rubber duck debugging!
Rubber duck debugging is a great technique with a lovely name. It works like this: Get a rubber duck, set it on your desk, and explain to the duck what your code is doing. Will people think you are crazy? Perhaps. But this is highly effective. Often, the experience of attempting to explain—out loud—what code should be doing is enough to make you realize why it is or is not working properly.
J Write JavaScript in external files.
Not only will it be easier to work with the JavaScript code when using exter-nal files (because you won’t have to hunt through HTML), the JavaScript debugger will be more likely to provide a correct line number.
J Save the file and refresh the browser!
If you fail to save your JavaScript file after making changes, or if you fail to reload the browser you’re running the JavaScript in, then the browser will not reflect the latest changes, and you’ll spend an eternity attempting to fix the problem.
J Try a different browser.
Some JavaScript errors you’ll encounter will be browser-specific. Until you really get comfortable with how the different browsers behave on a JavaScript level, get in the habit of running JavaScript code in multiple browsers. Isolating the specific browsers that are experiencing the problem can help you more quickly determine the underlying cause.
Conversely, if you see the same problem regardless of the browser, then you know the problem must be in the code itself.
J Take a break!
I’ve solved many harrowing problems not by doing anything on the com-puter but by stepping away from it. Take a walk. Eat an apple. When all else fails, do something other than continuing to actively debug the problem.
Often, the few minutes it takes to clear your head will allow you to come back to the problem with fresh eyes and a new approach.
ptg7799847 In terms of coding, there are a couple of techniques you can use that shouldn’t
be too advanced to introduce here. A simple, beginner’s way of debugging is to use alert() to notify you of a script’s progress, the value of variables, and so forth.
When you don’t know what’s going on in your code, adding an onslaught of alerts can really help (Figure 3.17):
alert(‘Now in the XXX function!’);
alert(‘myVar is ‘ + myVar);
On the other hand, alerts are unseemly and you can tire of having to always close them. A better alternative is to write those same messages to the JavaScript console. To do that, call the log() method of the console object, providing it with the message to be written (Figure 3.18):
console.log(‘Now in the XXX function!’);
console.log(‘myVar is ‘ + myVar);
Because the console log is nonintrusive, you can use it generously, such as to indicate each step in the logical process. For example, each step in the code could be marked by outputting a number:
// Start!
console.log(1);
// Some code.
console.log(2);
fiGURe 3 .17 Alert boxes are a simple and overt way to pro-vide debugging information.
fiGURe 3 .18 Writing messages to the console is another way of providing debugging data.
errorS and deBugging 85
ptg7799847 Alternatively, you can just invoke console.trace(). This function, used without
providing any additional information, sends a message to the console indicating the current function being executed (called a stack trace). For example, the following code would print the string init within the console when this function is called:
function init() { console.trace();
}
Finally, when using JavaScript in a networked manner, such as performing Ajax requests, using a browser or IDE with a network monitoring tool will be a great asset, letting you confirm:
J What requests are being made
J The data included in the requests
J The data included in the response.
You’ll also want to validate the received data when you’re having problems. For example, if the returned data is meant to be XML or JSON (you’ll learn about both in Chapter 11, Ajax), validating that the data is syntactically correct XML or JSON is a good step to take. More on this in Chapter 11.
using FireBug
Firebug has long been the savior of the Web developer. It’s free, has a ton of features, and continues to be well supported. I want to provide a brief introduction to using Firebug here, focusing solely on its JavaScript-related tools, but I recommend that you seek some online videos that visually represent this same information, as well as go into more details about Firebug.
Note that Firebug was originally developed for the Firefox Web browser. The Firebug Lite extension is now available for other browsers, but the full Firebug on Firefox is still the best. Although the Web developer tools now shipping with Safari (i.e., the Web Inspector), Opera (Dragonfly), and Internet Explorer (Developer Toolbar) are worthwhile, Firebug is the gold standard in this area and I’d be remiss not to give Firebug the preferential treatment it has earned.
ptg7799847 To open Firebug, you must have a browser window open, although not
necessar-ily with a Web page loaded in it. In the upper-right corner of the Firebug interface are three circles (Figure 3.19). Clicking the first (an inverted chevron) minimizes Firebug but keeps it active. Clicking the second (a standard chevron) opens Fire-bug in a separate window. Clicking the third (an X), closes FireFire-bug, thereby also making it inactive.
Within a blank Web page, you can use Firebug’s Console tab to execute any random bits of JavaScript. You can enter single lines of JavaScript code at the prompt at the bottom of the window, and the output will be displayed in the console. To test larger blocks of JavaScript, click the Command Editor icon in the lower-right corner (another chevron). Then you can insert larger blocks of code and execute it by clicking Run (Figure 3.20).
To apply Firebug to a Web page, load the page in your browser, and then bring up Firebug. If there are any errors in the page, or any console.log() output, you’ll see that information in Firebug’s Console panel. You can also enter JavaScript into the console to test aspects of the page, such as support for particular objects or the values of page variables.
Within the console, the inspect() function provides all of the information about a given variable:
inspect(someVar);
And you can enter clear(), to clear the console’s contents.
fiGURe 3 .19 Click these circles to control Firebug’s presence.
fiGURe 3 .20 Use the Firebug Console panel to execute single or multiple lines of JavaScript.
TIP: the single-line console prompt supports code completion.
errorS and deBugging 87
ptg7799847 For debugging purposes, the Script panel is a real time-saver. First, you can
select what JavaScript code to view in the Script panel, whether it’s an external file or inline. This is useful, but using Watch and Breakpoint capabilities is where the advanced debugging techniques come into play.
A breakpoint is a command to have the code stop executing at a certain point.
One of the hardest things about debugging JavaScript is that so much happens, and so quickly, that it’s difficult to know what’s causing a problem, what’s happening to various variables, what the logic flow is, and so forth. Breakpoints give you a way to pause the script’s execution so that you can take a look around.
For example, if you load the login form from Chapter 2, you’ll see that login.
js can be shown in Firebug’s Script panel. If there’s a problem with, say, the form validation, you can set a breakpoint inside of the validateForm() function to take a peek at that point in the process. To set a breakpoint in Firebug, just click on the line number beside the script, and a red circle will appear (Figure 3.21). Note that the breakpoint takes effect before that line is executed. In other words, if you set a breakpoint on line 25, line 24 will be the last executed line of code before the pause.
When Firebug encounters a breakpoint, you can turn your attention to the Watch tab in the right-side pane. By default, the Watch tab lists the variables that exist and their values at the moment of the break. This is a huge debugging asset.
For complicated variable types (e.g., objects), clicking the arrow beside the variable name reveals the properties and methods of that object (Figure 3.22).
At the top of the Script panel, there are five buttons where you can decide what to do next, after encountering a breakpoint (from left to right):
J Rerun been set on the JavaScript code.
fiGURe 3 .22 Use the Watch tab to see the variables that exist at the break, and their values.
ptg7799847 The meanings of these can be a bit confusing for those new to Firebug, so I’ll
just put them in simplest terms. Rerun restarts the execution of the code. Continue will continue the script’s execution until its end or another breakpoint is encoun-tered. Step Into, Step Over, and Step out all dictate whether the debugger will go into, over (i.e., not into), or out of the definition of the next function call. When you feel ready to learn more, see the Firebug Wiki (http://getfirebug.com/wiki/
index.php/Script_Panel) or search online.
Getting back to the breakpoints, another way of setting breakpoints is to click the icon in Firebug’s upper-left corner, which looks like a pause button with a small play button on it. This enables Firebug’s “Break On Next” setting, which means that Firebug will break on the next executed line. (There’s a similar icon on the Console panel for breaking on the next line that causes an error.)
Finally, you can set conditional breakpoints, which are watch expressions. For example, click New watch expression in the Watch pane, then enter window.onload in the text field (Figure 3.23). This establishes a breakpoint when the window.
onload event is triggered (you’ll need to reload the page to see this watch expres-sion be triggered). You can also create a watch expresexpres-sion by right-clicking (or Control+Clicking) on a breakpoint icon (the red circle to the left of a line number).
In the resulting pop-up, enter the condition that must be met for this breakpoint to take effect (Figure 3.24). Watch expressions are most commonly used to set breakpoints based upon the value of a variable.
I don’t want to overwhelm you with debugging JavaScript using Firebug when you don’t formally know the language in the first place, so that’s enough about Firebug for now. My recommendation is to get in the habit of using it, and slowly build up familiarity with its multitude of features. There are oodles of tutorials and screencasts online for how to use it, and you’ll see some more recommendations toward that end a time or two in this book.
fiGURe 3 .23 This new watch expression does not reference a specific breakpoint.
fiGURe 3 .24 This watch expression is for an existing breakpoint.
errorS and deBugging 89
ptg7799847