The Microsoft script debugger for use with IE is a very useful tool for helping discover what's gone wrong and why. Using it, you can halt the execution of your script and then step through code line by line to see exactly what is happening.
You can also find out which data is being held in variables and execute statements on the fly. Without the script debugger the best we can do is use the alert() method in our code to show the state of variables at various points.
Note |
The script debugger works with IE 3.02+ as long as you have the latest (5.6) version of the JavaScript and VBScript engines. The engines come automatically with IE 5.0+. |
We can currently download the script debugger from the following URL if we are running Windows 98 or ME:
Use this URL when running Windows NT, 2000, or XP:
If these URLs change, a search on the Microsoft website, http://www.microsoft.com, for "script debugger" ought to find its new home.
If Personal Web Server (PWS) or Internet Information Services IIS are installed, which we'll be looking at in a later chapter, the script debugger should already be installed. Other programs, such as Visual InterDev, which is part of Visual Studio, also automatically install the script debugger.
In addition, Windows 2000 automatically comes with the script debugger, although it may not be set up on your system. To install it open the Control Panel and choose Add/Remove Programs. Click the Add/Remove Windows Components button, and in the window that opens, scroll down the list to the script debugger. If it is not checked, then check it and click Next to install it.
To see if the script debugger is already installed, open up Internet Explorer and select the View menu. If one of the menu options is Script Debugger, as shown in Figure 10-3, the debugger is installed. If we do not find this menu option, it is still possible that the script debugger is already installed, but disabled. See the end of the next section for how to enable the script debugger, and then check for the menu option again.
After downloading the script debugger, we need to install it. First, we need to run the file we have just downloaded, for example from the Windows start bar's Run menu option. We should then see the dialog box shown in Figure 10-4 asking whether we want to install the debugger: click the Yes button.
Next the license screen appears, as shown in Figure 10-5. Check the license, and then click the Yes button to agree to the conditions and install the debugger.
Next we get to choose where we want to install the debugger. Anywhere on our local machine is fine. (See Figure 10-6.)
Click OK, and if a screen appears asking whether to create the directory, just click Yes.
The script debugger will now install. Once it's complete, we see the message shown in Figure 10-7.
Click OK and if asked whether to restart the computer, click Yes.
After the computer has restarted, open up Internet Explorer. If we go to the View menu, we should see the Script Debugger option. If not, the script debugger may be set as disabled. To enable it in IE 5 and later go to the Tools menu, or in IE 4 go to the View menu, and select Internet Options. Select the Advanced tab to see a screen similar to that shown in Figure 10-8.
Make sure that the Disable script debugging box is unchecked, as shown in Figure 10-8. If checked, uncheck it. Click OK, and then close the browser. When we reopen the browser we should see the Script Debugger option in the View menu.
It's important to point out that there are actually two versions of the script debugger: the basic version that we installed in the previous section and a more sophisticated version that comes with programs like Personal Web Server and Visual InterDev. The more sophisticated version does everything that the basic version does, but the screen layout and look will vary slightly, as will some of the keys and icons. We'll be looking at just the basic version here, so all screenshots are applicable to that.
We can open a page in the script debugger in a number of ways, but here we'll just look at the three most useful. However, before we start let's create a page we can debug. Notice that we have made a deliberate typo in line 14. Be sure to include this typo if creating the page from scratch.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <script language="JavaScript" type="text/javascript"> function writeTimesTable(timesTable) { var counter; var writeString; for (counter = 1; counter < 12; counter++) { writeString = counter + " * " + timesTable + " = "; writeString = writeString + (timesTable * counter); writeString = writeString + "<br>"; documents.write(writeString); } } </script> </head> <body> <div><script language=JavaScript type="text/javascript"> writeTimesTable(2) </script> </div> </body> </html>
Save this page as debug_timestable.htm.
When we load this page into Internet Explorer, we'll discover the first way of activating the script debugger: automatically when there is an error in our code.
We should see a message box, similar to that shown in Figure 10-9, asking whether we wish to debug. Click Yes. Note that if Visual Studio is installed, a second dialog box will give us the option of opening a project. Just click No at that point.
Having said that we want to debug, we should see the screen shown in Figure 10-10. The debugger has opened and stopped on the line where the error is and highlighted it in yellow, although this may not be obvious from the black and white screenshots in this chapter. The deliberate mistake is that we've written documents.write rather than document.write. The view is read-only so we can't edit it here, so we need to return to our text editor to correct it. Let's do that, and then reload the page.
Having corrected the mistake and reloaded the page, we should see the two times table in our web page, as shown in Figure 10-11.
The second method we're going to use to open the debugger is to load the page we want to debug into our browser; then we use the Break at Next Statement menu option under Script Debugger on Internet Explorer's View menu as shown in Figure 10-12.
We've already got debug_timestable.htm loaded, so select View ® Script Debugger ® Break at Next Statement. Not much appears to happen, but reload the page by clicking the refresh icon or pressing F5, and the debugger will open at the next JavaScript statement executed.
Where the next JavaScript statement occurs in our page depends on our code. If we have code in the page other than a function or code connected to an event handler, the first line the browser executes will be the next JavaScript statement. In our case, this is the code calling the function
<script>writeTimesTable(2)</script>
If there is no code in the page except code inside event handlers or functions, the next statement executed will be that fired in an event handler, such as the window object's onload event handler or a button's onclick event handler.
Note that with some set-ups of the script debugger, the browser brings up a dialog box saying that an exception of type "Runtime Error" was not handled. This does not mean that there is an error in the code, but simply is the way Break at Next Statement works.
As we can see from Figure 10-13, the next statement executed in our example occurs when the browser reaches the script embedded in our web page that calls the writeTimesTable() function, again highlighted in yellow by the debugger.
We're now going to use the Step Into icon, illustrated here, that can be found on the top toolbar.
Click this icon and the debugger will execute the current line of code and move to the next line, in this case our function. We'll look more fully at stepping through code and examining the contents of variables shortly.
Finally, let's look at a third way of opening the debugger, which is probably the easiest and most useful.
Imagine we want to stop our code's execution and open the debugger just before the for loop is executed. We can do this by simply adding the keyword debugger to our script, which will stop the execution at that point and open the debugger. Let's do that now.
We need to close the debugger, return to our text editor, and add the following code to debug_timestable.htm.
function writeTimesTable(timesTable) { var counter; var writeString; debugger for (counter = 1; counter < 12; counter++) { writeString = counter + " * " + timesTable + " = "; writeString = writeString + (timesTable * counter); writeString = writeString + "<br>"; document.write(writeString); } }
Now refresh the page in Internet Explorer, and we'll find that the debugger opens, with code execution paused on the line with the debugger keyword in it. (See Figure 10-14.)
Again we can click the Step Into icon we used previously, and watch the code execute statement by statement.
There are three important ways of stepping through code, each involving one of the icons from the top toolbar of the script debugger.
We've seen that one way is to step into the code. This simply means that every line of code is executed on a line-by-line basis. If a function is called, we step into that function and start executing the code inside the function statement by statement, before stepping out again at the end and returning to the calling line. To do this we use the Step Into icon.
We may find that having stepped into a function we get half way through and decide the function is not the source of the bug and we want to execute the remaining lines of code in the function, then continue step-by-step from the point at which the function was called. This is called stepping out of the function, and uses the Step Out icon.
There may also be times when we have some code with a bug in it that calls a number of functions. If we know that some of the functions are bug free, then we may want to just execute them and not actually step into them and see them being executed line by line. For this the debugger has the Step Over icon, which executes the code within a function but without us having to go through it line by line.
Let's alter our times table code in debug_timestable.htm and demonstrate the three methods of stepping action. Note that the debugger keyword has been removed from inside the writeTimesTable() function and is now in the second script block.
<html> <head> <script language=JavaScript type="text/javascript"> function writeTimesTable(timesTable) { var counter; var writeString; for (counter = 1; counter < 12; counter++) { writeString = counter + " * " + timesTable + " = "; writeString = writeString + (timesTable * counter); writeString = writeString + "<BR>"; document.write(writeString); } } </script> </head> <body> <div><script language=JavaScript type="text/javascript"> var timesTable; debugger for (timesTable = 1; timesTable <= 12; timesTable++) { document.write("<P>") writeTimesTable(timesTable) document.write("</P>") } </script></div> </body> </html>
Save this as debug_timestable2.htm. When we load this into our browser, the script debugger will be opened by the debugger statement, as shown in Figure 10-15.
Click the Step Into icon and code execution will move to the next statement. In this case, the next statement is the first statement in the for loop where we initialize the variable timesTable to the value of 1, as shown in Figure 10-16.
When we click the Step Into icon again, the timesTable = 1 statement is executed and we step to the next statement due to be executed, which is the condition part of the for loop. With timesTable set to 1, we know that the condition timesTable <= 12 is going to be true. Click the Step Into icon and the condition executes and indeed we find we're right, the condition is true and the first statement inside the for loop, document.write("<P>"), is next up for execution.
When we click the Step Into icon again it will take us to the first calling of our writeTimesTable() function. We want to see what's happening inside that function, so click Step Into again and we'll step into the function. Our screen should look like the one shown in Figure 10-17.
The next statement to be executed is not the var counter; or var writeString; lines, but instead the for loop's initialization condition. The first two lines have been executed, but the script debugger does not allow us to step variable declarations line by line.
Click the Step Into icon a few times and to get the gist of the flow of execution of the function. In fact, stepping through code line by line can get a little tedious. So let's imagine we're happy with this function and want to run the rest of the function. Start single-stepping from the next line after the function was called. To do this, click the Step Out icon, which we discussed previously.
Now the function has been fully executed, and we're back out of it and at the next line, document.write("</P>"), as we can see from Figure 10-18.
Click the Step Into icon to see that the document.write() line will be executed and the next statement in the flow of execution is the increment part of our for loop. Click Step Into again and execution will continue to the condition part of the for loop. Clicking Step Into twice more brings us back to the calling of the writeTimesTable() function. We've already seen this in action, so really we want to step over it and go to the next line. Well, no prizes for guessing that the Step Over icon that we talked about previously is what we need to click to do this.
Click the Step Over icon and the function will be executed, but without us having to step through it statement by statement. We should find ourselves back at the document.write("</P>") line.
If we've finished debugging, we can run the rest of the code without stepping through each line by clicking the Run icon on the toolbar, which we illustrate here. (However, note that when using Visual InterDev, it's a play button icon like that on a video recorder.) Let's do that; then we can return to the browser and see the results of the code we have executed. We should see a page of times tables from 1*1=1 to 11*12=132 in the browser.
Breakpoints are markers we can set in the debugger that force code execution to stop at that point and start single-stepping through the code.
Load our debug_timestable2.htm page into the browser. This will open the debugger and stop execution at the line with our debugger statement. Now imagine we want to stop in our writeTimesTable() function on the line that writes the results of the times table to the page, namely document.write(writeString), as shown in Figure 10-19. This is the last statement in the for loop. However, we're busy people and we don't want to manually step through every line before that. What we can do is set a breakpoint on that line and then click the Run icon, which will restart the code execution in the normal fashion, that is, without single stepping. Then when the breakpoint is reached, code execution will stop and we can start single-stepping if we want.
To set the breakpoint, we need to scroll up the code window in the debugger until we can see the line on which we want to put the breakpoint. Click that line; then click the Toggle Breakpoint icon on the toolbar, illustrated here.
Any line with a breakpoint on it is indicated by the reddish-brown dot on the left of the code window and by the line itself being set to a reddish brown, although the line may not always be colored. We can set as many or few breakpoints at one time as we wish, so if we want to break on other lines we can add breakpoints there too.
To unset a breakpoint we just click the relevant line of code and click the Toggle Breakpoint icon again, and that toggles it off. To clear all breakpoints at once we can click the Clear All Breakpoints icon illustrated here. (See Figure 10-19.)
Okay, now let's start the code running again by clicking the Run icon in the toolbar.
We find that code resumes executing without single-stepping until it reaches our breakpoint, at which point it stops. We can either single-step using the Step Into icon or click the Run icon again, in which case execution continues unless a breakpoint is reached again.
Leave the debugger open and with code execution halted on our breakpoint; we'll be using it in a moment.
While stepping through code and checking that its flow of execution is useful, what would be really useful is the ability to check the values contained inside variables, evaluate conditions, and even change things on the fly. We can do all of these things using the debugger's command window.
Hopefully you still have the debugger open with execution halted at the breakpoint we set previously. The line stopped on in Figure 10-19 is repeated here:
document.write(writeString);
Let's see how we can find out the value currently contained in the variable writeString.
First we need to open the command window from within the debugger. We do this by clicking the Command Window icon, illustrated here, or by selecting Command Window from the View menu.
In the command window, type the name of the variable we want to examine, in this case writeString; then click Enter. This will cause the value contained in the variable to be printed below our command in the command window, as shown in Figure 10-20.
If we want to change a variable, we can write a line of JavaScript into the command window and press Enter.
For example, type the following into the command window and click Enter.
writeString = "Changed on the Fly<BR>"
Now remove the breakpoint (see the previous instructions for how to do this) and click the Run icon. If we switch to the browser, we see the results of our actions: Where the 1*1 times table result should be, the text we changed on the fly has been inserted. Note that this does not change our actual HTML source file, just the page currently loaded in the browser.
The command window can also evaluate conditions. Refresh the browser to reset the debugger and leave execution stopped at our debugger statement. Click the Step Into icon twice and execution will stop on the condition in the for statement.
Type the following into the command window and press Enter.
timesTable <= 12
Because this is the first time the loop has been run, as shown in Figure 10-21, timesTable is equal to 1 and so the condition timesTable <= 12 evaluates to true. Note that the debugger sometimes represents true by the value -1 and false by the value 0, so we may see the value -1 instead of true.
We can also use the command window to access properties of the browser's Browser Object Model (BOM). For example, if we type window.location.href into the command window and press Enter, it will tell us where the current page is stored.
In fact, the command window can execute any single line of JavaScript including functions.
When single-stepping through the code, the call stack window keeps a running list of which functions have been called to get to the current point of execution in the code.
Let's create an example web page that demonstrates the call stack very nicely.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <script language=JavaScript type="text/javascript"> function firstCall() { secondCall(); } function secondCall() { thirdCall(); } function thirdCall() { // } function button1_onclick() { debugger firstCall(); } </script> </head> <body> <input type="button" value="Button" name=button1 onclick="return button1_onclick()"> </body> </html>
Save this page as debug_callstack.htm, and then load it into IE. When loaded, all we'll see is a blank web page with a button. Click the button and the debugger will be opened at the debugger statement in the button1_onclick() function, which is connected to the button's onclick event handler.
To open the call stack window, click the Call Stack icon in the toolbar, illustrated here, or choose Call Stack from the View menu.
Our debugger now looks like Figure 10-22.
Every time a function is called, the debugger adds the function to the top of the call stack. We can already see that the first function called was actually the code attached to the onclick event handler of our button. The anonymous function is the event handler code that calls our onclick function. Next, added to the call stack is the function called by the onclick event, which is the function button1_onclick() shown at the top of the call stack.
If we want to see where each function was first entered, we just need to double-click the function name in the call stack window. Double-click <Jscript> – Jscript – anonymous function and the calling line, that is, the code connected to the onclick attribute of the <input> tag, will be shown. Now double-click the top line <Jscript> – button1_onclick and that will take us back to the current execution point.
Now single-step twice, using the Step Into icon. The first step is to the line that calls the firstCall() function. The second step takes us into that function itself. The function is immediately added to the call stack, as shown in Figure 10-23.
Click the Step Into icon again and we'll step into the second function, secondCall(). Again this is added to the call stack. One more click takes us into the third function, thirdCall(), again with its name being added to the top of the call stack.
Now click Step Into again and as we leave the function thirdCall(), we see that its name is removed from the top of the call stack. Another click takes us out of the second function secondCall() whose name is also now removed from the stack. Each additional click takes us out of a function, with its name removed from the call stack, until eventually all code has been executed and we're back to the browser again.
Our demo page was very simple to follow, but with complex pages, especially multi-frame pages, the call stack can prove very useful for tracking where we are, where we have been, and how we got there.
The final window we'll look at is the running documents window. This window lists each instance of Internet Explorer running and which pages, or documents, are currently loaded in that instance of IE.
Let's create some example pages demonstrating the use of the running documents window.
The running documents window proves most useful with frame-based pages, so let's create a page with two frames inside it.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd"> <html> <frameset rows="50%,*"> <frame name="topFrame" src="debug_topFrame.htm"> <frame name="bottomFrame" src="debug_bottomFrame.htm"> </frameset> </html>
This first page defines the frameset. Save it as debug_frameset.htm.
Next, we see the page for the top window.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>example</title> <script language=JavaScript type="text/javascript"> function button1_onclick() { var x; x = 1 + 1; alert(x) } </script> </head> <body> <h2>Top Frame</h2> <input type="button" value="Button" name=button1 language=JavaScript onclick="return button1_onclick()"> </body> </html>
Save this page as debug_topFrame.htm.
Finally, we enter the third page.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <script language=JavaScript type="text/javascript"> function button1_onclick() { var x; x = 2 * 2; alert(x); } </script> </head> <body> <h2>Bottom Frame</h2> <input type="button" value="Button" name=button1 language=JavaScript onclick="return button1_onclick()"> </body> </html>
Save this page as debug_bottomFrame.htm.
Now load debug_frameset.htm into the browser. You will see two frames, each containing a button.
We can view all the pages currently loaded in the frames by first opening the debugger by choosing Script debugger ® Open from the View menu. Now with the debugger open we need to view the running documents window by clicking the Running Documents icon in the toolbar, illustrated here, or selecting Running Documents from the View menu.
This will initially show each instance of Internet Explorer running on the machine. Click the plus sign (+) to open a window that shows which pages are currently loaded, or running, in that instance of Internet Explorer. When pages are in framesets, as ours are, then pages contained within the window frameset page are included, indented underneath the frameset page. (See Figure 10-24.) We'll need to click the plus sign again to open them.
The easiest way to debug the code within the top frame is to right-click debug_topFrame.htm in the Running Documents window and select Break at Next Statement. Then click the button in the top frame and the debugger will open, with execution stopped at the first statement due to be executed (onclick="return button1_onclick()").
That concludes our brief tour of the Microsoft script debugger. This debugger is excellent for debugging pages so that they will work in IE. However, it won't help us spot errors due to cross-browser incompatibility. For example, what works in NN might throw an error in IE, and a page that we've debugged successfully in IE could easily throw an error in NN. Aside from this problem, the debugger is a great help for spotting logic errors in our code.