Try an online JavaScript class for free!
Additional Resources

Error Handling and Debugging

In this lesson of the JavaScript tutorial, you will learn...
  1. That errors can be trapped and dealt with
  2. To use Firebug to trace and resolve errors

No matter how careful you are, it always seems that errors find their way into your code. Sometimes they are runtime errors caused by unpredicted scenarios. Sometimes the errors are just incorrect behavior of your code, popularly known as bugs.

Fortunately, we have tools to deal with either type of problem. In this lesson we will talk about detecting and handling errors and also about tracing down bugs and take them out of our applications.

Runtime Errors

Web browsers are such an hostile environment that it is almost guaranteed that we will constantly deal with runtime errors. Users provide invalid input in ways you didn't think of. New browser versions change their behavior. An AJAX call fails for a number of reasons.

Many times we can't prevent runtime errors from happening, but at least we can deal with them in a manner that makes the user experience less traumatic.

Completely unhandled errors

Look at this seemingly trivial code snippet.

function getInput() {
 var name = window.prompt('Type your name', '');
 alert('Your name has ' + name.length + ' letters.');
}

It may not be obvious, but this code has a bug waiting to break free. If the user clicks Cancel or presses Esc the prompt() function will return null, which will cause the next line to fail with a null reference error.

If you as a programmer don't take any step to deal with this error, it will simply be delivered directly to the end user, in the form of a utterly useless browser error message like the one below.

Error dialog

Depending on the user's browser or settings, the error message may be suppressed and only an inconspicuous icon shows up in the status bar. This can be worse than the error message, leaving the users thinking the application is unresponsive.

Error icon

Globally handled errors

The window object has an event called onerror that is invoked whenever there's an unhandled error on the page.

window.onerror = function (message, url, lineNo) {
 alert(
  'Error: ' + message + 
  '\n Url: ' + url + 
  '\n Line Number: ' + lineNo);
 return true;   
}

As you can see, the event will pass 3 arguments to the invoked function. The first one is the actual error message. The second one is the URL of the file containing the error (useful if the error is in an external .js file.) The last argument is the line number in that file where the error happened.

Returning true tells the browser that you have taken care of the problem. If you return false instead, the browser will proceed to treat the error as unhandled, showing the error message and the status bar icon.

Here's the message box that we will be showing to the user.

Error message

Structured Error Handling

The best way to deal with errors is to detect them the closest possible to where they happen. This will increase the chances that we know what to do with the error. To that effect JavaScript implements structured error handling, via the try...catch...finally block, also present in many other languages.

Syntax
try {
    statements;
} catch (error) {
    statements;
} finally {
    statements;
}

The idea is simple. If anything goes wrong in the statements that are inside the try block's statements then the statements in the catch block will be executed and the error will be passed in the error variable. The finally block is optional and, if present, is always executed last, regardless if there was an error caught or not.

Let's fix our example to catch that error.

function getInput(){
 try {
  var name = window.prompt('Type your name', '');
  alert('Your name has ' + name.length + ' letters.');   
 } catch (error) {
  alert('The error was: ' + error.name + 
  '\n The error message was: ' + error.message);
 } finally {
  //do cleanup
 }
}

The error object has two important properties: name and message. The message property contains the same error message that we have seen before. The name property contains the kind of error that happened and we can use that to decide if we know what to do with that error.

With that in place, if we reload the page and cancel out of the prompt, that's what we will see:

Error message

It's a good programming practice to only handle the error on the spot if you are certain of what it is and if you actually have a way to take care of it (other than just suppressing it altogether.) To better target our error handling code, we will change it to only handle errors named "TypeError", which is the error name that we have identified for this bug.

function getInput(){
 try {
  var name = window.prompt('Type your name', '');
  alert('Your name has ' + name.length + ' letters.');   
 } catch (error) {
  if (error.name == 'TypeError') {
   alert('Please try again.');
  } else {
   throw error;
  }
 } finally {
  //do cleanup
 }
}

Now if a different error happens, which is admittedly unlikely in this simple example, that error will not be handled. The throw statement will forward the error as if we never had this try...catch...finally block. It is said that the error will bubble up.

Throwing custom errors

We can use the throw statement to throw our own types of errors. The only recommendation is that our error object also has a name and message properties to be consistent in error handling.

throw {
 name: 'InvalidColorError', 
 message: 'The given color is not a valid color value.'
};

Debugging

One of the most important activities in software development is debugging. It can also be one of the most costly. That's why we need to do our best to reduce the amount of time spent in debugging.

One way to reduce this time is to create automated unit tests, which we will see in the lesson Production Grade JavaScript.

Another way is to use the best tools available and try to remove the pain associated with debugging. It used to be the case that debugging tools for JavaScript were archaic or close to non-existent. This situation has improved a lot and now we can confidently say we have feasible ways to debug JavaScript without resorting to horrendous tactics, such as sprinkling alert() calls across our code.

We won't waste your time discussing all the existing tools for debugging. Instead we will focus on the tool that singlehandedly re-wrote the JavaScript debugging history.

Firebug

Firebug is an extension for the Mozilla Firefox browser. Once installed, Firebug will turn Firefox into almost an IDE for web development.

Let's learn about Firebug's capabilities by debugging an issue in practice.

Exercise: The Background Highlighter

Duration: 30 to 45 minutes.

In this exercise we are trying to understand why our BackgroundHighlighter object is not working as expected. The object is supposed to change the background color of an input field when it gets focus and revert it when it loses focus.

For some reason the code is not working. Here is the code.

Code Sample: ErrorHandlingAndDebugging/Exercises/bgnd-changer.html

<html>
<head>
 <title>Background Highlighter</title>
 <style type="text/css">
  .wc_debug
  {
   background-color:#ffc;
  }
 </style>
 <script type="text/javascript" src="../../Libraries/DebugHelp.js" ></script>
 <script type="text/javascript">
  //add the debud panel at the bottom of the page
  observeEvent(window, 'load', function () {insertDebugPanel();} );
 </script> 
 
 <script type="text/javascript">
  var BackgroundHighlighter = function (field, color) {
   this.field = document.getElementById(field);
   this.color = color;
   
   this.field['onfocus'] = this.onGotFocus;   
   this.field['onblur'] = this.onLostFocus;    
  };
  BackgroundHighlighter.prototype = {
   onLostFocus: function () {
    this.field.style.backgroundColor = '';
   },
   onGotFocus: function () {
    this.field.style.backgroundColor = this.color;
   }
  };
  
  function onPageLoad() {
   //this function runs as soon as the page loads
   new BackgroundHighlighter('userName', '#ff9');
   new BackgroundHighlighter('company', '#ff9');   
  }
  
  observeEvent(window, 'load', onPageLoad );
  
 </script>
 
</head>

<body>
 <form action="#">
  Name: <input type="text" name="userName" value="" id="userName"/> <br/>
  Company: <input type="text" name="company" value="" id="company">
 </form>
</body>
</html>
  1. Open the above file in Firefox.
  2. Activate Firebug. Tools menu, Firebug, uncheck Disable Firebug (if checked) then Tools menu, Firebug, Open Firebug.

Now if you click the Name textbox, Firebug will tell you that there's an error. See the picture below and note the error message in the status bar.

Firebug shows error

The Console tab in Firebug shows that the error message is "this.field has no properties". The error problem seems to be on line number 32.

Expand the error message by clicking the "+" icon next to it. We will get one extra piece of information, the Call Stack, which in our case is simply one method call onGotFocus() as we can see in the image below.

Firebug shows stack trace

When we click on onGotFocus() we will jump to the actual line of code in the Script tab. Let's place a breakpoint on that line by clicking on the gutter on the left margin, right to the left of the line number. Breakpoints are represented by a red circle on that margin.

Firebug shows breakpoint

Now let's click on the Name field again. Firebug kicks in an halts execution at the breakpoint we just set.

Firebug breaks on the breakpoint

Looking at the Watch tab on the right, we can see that it is already tracking the value of this. And, to our surprise, this does not contain a reference to an instance of our BackgroundHighlighter. Instead it contains a reference to the input element.

Remember when we said you should be careful when using the this keyword in our objects? That was back in The perils of this. Well, that is precisely the problem we are having right now. Our onGotFocus() method is being called as an event handler for the onfocus event of the input field, and that call is made with the input field being the value of this.

Our problem is not on line 32 though. The problem is back a few lines before:

this.field['onfocus'] = this.onGotFocus;   
this.field['onblur'] = this.onLostFocus;

We cannot just pass a reference to one of our methods like that. We need to create some context that forces the this inside those methods to contain our object. This is not hard. Let's change those two lines to:

var that = this;

this.field['onfocus'] =  function() { 
 that.onGotFocus();
}; 

this.field['onblur'] = function() { 
 that.onLostFocus();
};

Now save the page and refresh the browser. The page should work now.

The Console tab

The console tab is an interactive interface between you and the executing code. It contains a prompt denoted by >>> where you can inspect objects and variables, change their values, create new ones, etc.

Inspect DOM elements

The HTML tab allows us to inspect the page's structure. As we click elements in the displayed tree, they are highlighted in the page right above. It is very useful to understand where in the page hierarchy a given element is.

Inspect CSS

The CSS tab allows you to see all the CSS rules that exist on the page, from the various files that may contain CSS, and you can change or disable any individual attribute. The effects of changing the attributes are immediately reflected on the page. This feature is great for tweaking the CSS rules of your page to fix CSS bugs.

All your scripts

Using the Scripts tab you can see all the scripts in the page and set breakpoints to assist during debugging.

Network traffic

One of the most interesting tabs is the Net tab. It shows all the requests made during the load and operation of the page.

Firebug shows the network traffic

This tab is especially useful during the debugging of AJAX calls, where after expanding one of the requests we can see all the HTTP traffic information for that particular request. The HTTP headers can become very useful when tracing a problem.

Firebug shows HTTP headers

Firebug has many more features but you're better off playing with it and learning which ones become more useful to you. This tool is under active development, so make sure you check their site often to get newer versions as they become available.

Error Handling and Debugging Conclusion

Dealing with software errors in one of those skills that can always be polished more. Beyond any natural aptitude, the key to be effective in error handling is understanding the language support and the existing development tools.

In case we didn't make it clear enough, we believe you should choose Firefox as your main development browser and install Firebug immediately.

To continue to learn JavaScript go to the top of this page and click on the next lesson in this JavaScript Tutorial's Table of Contents.

Use of http://www.learn-javascript-tutorial.com (Website) implies agreement to the following:

Copyright Information

All pages and graphics on Website are the property of Webucator, Inc. unless otherwise specified.

None of the content on Website may be redistributed or reproduced in any way, shape, or form without written permission from Webucator, Inc.

No Printing or saving of pages or content on Website

This content may not be printed or saved. It is for online use only.


Linking to Website

You may link to any of the pages on Website; however, you may not include the content in a frame or iframe without written permission from Webucator, Inc.


Warranties

Website is provided without warranty of any kind. There are no guarantees that use of the site will not be subject to interruptions. All direct or indirect risk related to use of the site is borne entirely by the user. All code and explanations provided on this site are provided without warranties to correctness, performance, fitness, merchantability, and/or any other warranty (whether expressed or implied).


For individual private use only

You agree not to use this online manual to deliver or receive training. If you are delivering or attending a class that is making use of this online manual, you are in violation of our terms of service. Please report any abuse to courseware@webucator.com. If you would like to deliver or receive training using this manual, please fill out the form at http://www.webucator.com/Contact.cfm