Sunday, July 5, 2009

JavaScript Debugging Techniques in IE 6

Microsoft’s Internet Explorer 6 is almost universally hated by web developers. It’s hard to work with and support, but with a few solid techniques, you can make the process less painful. What “just works” in the majority of browsers will almost always require hours of tweaks and workarounds to get it working in IE6. With more and more users switching over to newer alternatives such as IE8, Safari and Firefox hopefully support for IE6 can be dropped sooner rather than later. In the mean time though many of us have to make sure our sites work in this awful browser.
To make things worse, IE6 is extremely bad at helping developers diagnose problems. When a JavaScript error occurs, IE6’s default behaviour is to display a small error icon in the status bar. Extremely easy to miss!


Double clicking on this icon will display a popup, and if you then click the “Show Details” button you’ll get the actual details of the JavaScript error. Unfortunately the detailed error message can be quite cryptic, and probably not too much help in diagnosing the actual problem. Your best bet is to make a note of the line and column number of the problem and then look that up in the source code. Fortunately, with the help of Visual Studio and by changing a few IE settings, we can make it much easier on ourselves.

Setting things up
To enable JavaScript debugging in IE we need to change some default settings, which can be accessed from the Tools> Internet Options menu, and then the Advanced tab. The image below shows the default IE6 settings, with the three settings we’re interested in highlighted.


Script debugging in IE needs be enabled, which requires us to disable the first option out of the three. If you also want to debug scripts in other contexts (such as Outlook) then disable the second option. Enable the third option if you’d like the JavaScript error dialog to pop up automatically instead of being displayed as a small icon in the status bar.



After changing those settings and restarting IE, we’re ready to start debugging. All of the following examples show Visual Studio 2005, but the same applied to 2008. You can also use the free Web Developer Express Edition if you don’t own a full copy of Visual Studio. Instead of IE prompting you to open the debugger, as it does in the examples below, you’ll first need to create a project and then start the debugger yourself. From that point on everything is the same.

Basic debugging
Let’s start with a simple example. The code below tries to call a function that doesn’t exist:
<script type="text/JavaScript">
functionDoesNotExist();
</script>
Now when we load that up into the browser instead of the tiny icon in the status bar IE will prompt us to open a debugger.


Selecting Visual Studio and clicking the yes button will open Visual Studio and highlight the code that is the cause of the problem.


For relatively simple errors, such as the one in the example above, just seeing the actual cause of the error highlighted is probably enough for us to work out what has gone wrong, and how to fix it. In more complex cases, however, we’ll need more information. This where the Visual Studio debugging features come in really handy.
In the code below we have a function, performAction, which takes a function as an argument and calls that function once it’s done. You’ll commonly see this sort of thing in JavaScript with asynchronous functions such as setTimeout and jQuery’s Get. There is a problem with the code below though: We’re accidentally passing in a string to performAction rather than a function.
<script type="text/JavaScript">
/**
* Perform an imaginary action, and then
* call the callback function once we're done
*/
function performAction(callback) {
// Perform an action here
// ...
// We're done, call the callback
callback();
}
// Call performAction with an anonymous
// callback function which contains an error
var myFunc = "this is a string, not a function!";
performAction(myFunc);
</script>

If we load up the code in IE and then debug it in Visual Studio as we have before we’ll see that the code “callback();” is highlighted, and is therefore the cause of problem. It won’t be obvious why this line is causing the problem though, not unless we know what the value of callback is. If callback was actually a function then there wouldn’t be a problem at all. The problem has only arisen because we accidentally passed in a string. This is where the “locals” window comes in handy. It lists all local variables along with their value and type. To display this window go to the Debug menu, then Windows, and then select Locals.

With the locals window displayed it’s clear that the problem is that callback is a string, rather than a function. The locals window doesn’t just support basic types such as integers and strings. If you’ve got a local object you can look at all of its properties and see the value and type of each of them, as we’ll see in the next example.



The ‘debugger’ keyword
The ‘debugger’ keyword, also supported by the Firefox JavaScript debugger Firebug, can really help us to track down problems. Below is a slightly modified version of the previous example, where this time the callback function is passed an object that should include a status and message property. The message property has been misspelled though:
<script type="text/JavaScript">
/**
* Perform an imaginary action, and then
* call the callback function once we're done
*/
function performAction(callback) {
// Perform an action here
// ...

// We're done, call the callback
callback({success: true, mesage: 'The action was performed!'});
}

// Call performAction with an anonymous
// callback function which contains an error
performAction(function(response) {
alert(response.message);
});
</script>

Because ‘message’ has been misspelt as ‘mesage’ in the performAction function when we run this code in IE we’ll see a popup alert with the message “undefined”, rather than the actual message we were hoping to see.
In a large project it can be difficult to track down why a property we expect to exist is undefined. Instead of trawling through lots of code we can simply add the line “debugger;” above the alert statement. IE will then automatically prompt us to debug the code in Visual Studio when it hits that line. From the locals window we can see that the ‘message’ property has been misspelt.


Using the call stack window, which can be accessed in the same way we accessed the locals window, shown at the bottom right of the above screenshot we can work out exactly where this problem occurred. If we double click on the ‘performAction’ line, the previous statement in the call stack, we’ll see the source of the error.

While debugger statements can be a useful tool in helping you track down problems you should be careful not to leave them in your production code!

More advanced debugging
So far we’ve covered some basic examples, such as calling non-existent functions and undefined properties. Using the locals and call stack windows it was relatively straight forward to track these issues down. There are times when the techniques we’ve discussed so far can’t help though. We can’t see what global variables exist or what value they have with the locals window, for example, and the call stack doesn’t always handle remotely loaded or dynamically executed code very well.
For these situations we must turn to the immediate window. It is by far the most powerful debugging tool that Visual Studio has to offer, allowing us to evaluate JavaScript expressions on the fly.The immediate window can be accessed in the same way that we accessed the other windows. Clicking inside the window will give it focus. We can then type JavaScript statements directly into the window and see the result. For example, type “window” followed by enter and you’ll get a print out of everything in the global scope.

When combined with the JavaScript arguments variable we can look in detail at all the current function arguments and the calling function. While the code in the next example isn’t something you’re likely to see in a real project, it does help to demonstrate techniques that can be extremely useful.

<script type="text/JavaScript">
/**
* This function takes a variable number of arguments.
*/
function performAction() {
// We're just interested in the immediate
// window, start the debugger
debugger;
}

// Call performAction with 4 different arguments, each of a different type
performAction('argument 1', 2, {argument: 3}, function() { return 4; });
</script>
We’ll load this up into IE and then go straight to the immediate window once we’ve opened up the debugger. If we type “arguments” followed by enter we’ll see the following information displayed:

arguments
{...}
[0]: "argument 1"
[1]: 2
[2]: {...}
caller: null
length: 4
The “{…}” tells us that arguments is an object. We also see this next to [2], so we know that the third argument (in array position 2) is an object. We can see what properties this object has by simply typing its name into the window:
arguments[2]
{...}
argument: 3
The fourth argument wasn’t listed in the original output because it’s a function. We know it exists though because the length property tells us that there are 4 arguments. We can also confirm it is there using typeof:
typeof arguments[3]
"function"
Unfortunately the immediate window isn’t quite so helpful with functions as it is objects, and if we type in the name of the function we just see “{…}” rather than any details of the function itself. Fortunately there is a handy workaround. If we type “alert(arguments[3]);” then the function code will be displayed in an alert dialog:
Hopefully these examples have conveyed just how powerful the immediate window is.

Summary
With the help of Visual Studio it is possible to transform IE6 from obscure error messages into an effective debugger. By using the local and call stack windows we can quickly and easily track down the majority of problems. The debugger statement allows us to automatically jump into the debugger at any point in our code. Should we come up again a more complex where the other tools can’t help us then we can always turn to the immediate window, which allows us to execute arbitrary JavaScript to inspect the current environment.


Reference
1) JavaScript Debugging Techniques in IE 6


No comments: