Thursday, March 28, 2024

Browser-specific Error Catching in JavaScript

There are many compelling reasons why error handling should play an integral part in your application development. The JavaScript Error Handling: Why You Need it article outlined some of the major reasons why you need to consider error handling sooner rather than later in a Web application’s development cycle.

Everyone knows that error handling is important, but to what end? In fact, error handling plays many roles, from reassuring the end-users, to helping identify trouble spots in an application, to siphoning off potential security holes in the code. In this installment on error handling in JavaScript, we’ll be taking a look at how to code your exception handling in JavaScript in a way that takes browser discrepancies into account.

Anatomy of the Try/Catch Block

In all browsers, the mechanism of choice for trapping errors is the Try/Catch block. As the name suggest, the Try/Catch Block is made up of two sections. The try {} encloses code in which to trap exceptions; the catch {} section contains code to deal with the error:

try {
  eval("var " + varName + "=" document.inputForm.txtVal);
} catch (e) {
  console.log("An exception occurred in the script."); 
}

It all seems so easy. However, there is a glitch in the above code; Internet Explorer doesn’t recognize the console object. The alternative of using the fully supported alert box is tempting, but don’t do it! The whole point of catching errors in the first place is to get rid of those annoying popups that appear every time an error fires. Instead, consider writing error messages to a designated part of the page or, in the case of validation errors, you can display the message right beside the field in question.

What the Error Object Can Tell Us

Once you’ve caught an Error, you can’t just display it directly. Doing so will call its inherited Object toString() method which may (or may not) display the highly informative “[Object Error]” representation. For best results, it’s better to access an error’s properties directly. There are several to choose from, depending on the browser you are writing for. I raised an error in several different browsers and inspected its properties. Here is what I found:

The Code

try {
  eval('alert("Hello world)'); //missing closing quote
} 
catch(error) {
  for (var prop in error) 
    document.writeln( prop + ": " + error[prop] + "</br>");
}

Internet Explorer 8

  • name: SyntaxError
  • message: Unterminated string constant
  • number: -2146827273
  • description: Unterminated string constant

Mozilla Firefox 9

  • fileName: file:///E:/unprotected/J%20Error%20Handling%20part%20II.htm
  • lineNumber: 69

Strangely, it does recognize the message property when accessed directly (error.message) and displayed “unterminated string literal” for my generated error.

Opera 11.60

Opera only supports the message property, but packs a wallop of information in there!

  • message: at index 19 in “alert(“Hello world)”: in string literal: : unexpected end of script

Nesting Try/catch Blocks

It is permissible to nest try/catch blocks so that even the error handler has an error handler! This comes in handy when you want to inspect objects in an effort to trace the probable cause of the error. Of course, since something went wrong, attempting to access an object’s properties may trigger the very same error a second time! Rather than try to anticipate every conceivable condition that could have caused the error, it’s a lot easier to just wrap the debugging code in a try/catch block of its own:

var something;
try {
  eval('alert("Hello world)');
} 
catch(error) {
  try {
    alert("DEBUGGING: something's property has a value of " + something.property + ".");
  }
  catch (e) {
    alert("something was not initialized. Aborting…");
  }
}

The Catch-all Error Handler

Trapping errors as close to the source is called Structured Error Handling. It’s generally a good idea to do this because the closer you are to the error’s cause, the better equipped you’ll be to deal with it. Having said that, it’s not really practical to wrap every line of code in try/catch blocks. For those errors that come out of unprotected code, there’s a global onerror event handler available via the window object. It is only invoked when an unhandled error occurs, so local try/catch blocks take precedence. Unlike the catch block, which receives the whole Error object, the onerror event receives three arguments, the message, url, and the lineNo:

window.onerror = function (message, url, lineNo) {
 alert('Error: ' + message + 
       'nUrl: ' + url + 
       'nLine Number: ' + lineNo);
 return true;   
}
//unprotected code
var something;
alert("Something's property has a value of " + something.property + ".");

The Global Error Handler worked very much the same in all three browsers that I tried it in. The only aberration was that Opera reported a line number of 11 instead of 97. Looks like it’s counting from the opening <script> tag:

Internet Explorer 8

Error: 'something.property' is null or not an object
Url: file:///E:/unprotected/J%20Error%20Handling%20part%20II.htm
Line Number: 97

Mozilla Firefox 9

Error: something is undefined
Url: file:///E:/unprotected/J%20Error%20Handling%20part%20II.htm
Line Number: 97 

Opera 11.60

Error: Uncaught exception: TypeError: Cannot convert 'something' to object
Url: file://localhost/E:/unprotected/J%20Error%20Handling%20part%20II.htm
Line Number: 11

And Finally…

There is a third part to a try/catch exception handler and called “finally”. The finally block comes after the try/catch and executes immediately following the code in the try block whether an error is encountered or not. Thus, it’s a useful place for performing house cleaning after some code inside the try block.

As you may have heard, HTML5 supports working with files via the FileReader. In the following example, the finally code makes sure that the file is always closed whether an error occurs or not:

openFile()   
try {     
   writeToFile(text);  
   //do some other stuff…
   
   return true; //this won't happen 
}  
catch(err) {
   spanErrors.innerHTML = "Couldn't write to the file";
} 
finally {   
   closeFile(); // always close the resource   
}  

Conclusion

That concludes the basics of JavaScript error handling. With the knowledge that you’ve gained here, you will now be able to manage your scripts much more adeptly. But don’t get too complacent; there is still a whole other level of error handling, which is to differentiate between the six Error subtypes. If you’re interested, we’ll be looking at each of those in detail the in the next article. Hope you’ll join us then!

Robert Gravelle
Robert Gravelle
Rob Gravelle resides in Ottawa, Canada, and has been an IT guru for over 20 years. In that time, Rob has built systems for intelligence-related organizations such as Canada Border Services and various commercial businesses. In his spare time, Rob has become an accomplished music artist with several CDs and digital releases to his credit.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Popular Articles

Featured