Decisions and Debugging - Client-Side Programming with JavaScript - HTML5 and CSS3 All-in-One For Dummies (2014)

HTML5 and CSS3 All-in-One For Dummies (2014)

Book IV Client-Side Programming with JavaScript

Chapter 3 Decisions and Debugging

In This Chapter

arrow Making decisions with conditions

arrow Working with nested if statements and switch

arrow Repeating with for loops

arrow Repeating with while loops

arrow Understanding the difference between bugs and crashes

arrow Using the debugger console

arrow Debugging your programs

Computer programs are complex. They involve information. Variables (described in Chapter 1 of this mini-book) are the foundation of information (although you'll learn about some more complex and interesting data types in later chapters). The other key component of programming iscontrol — that is, managing the instructions needed to solve interesting complex problems. In this chapter, you learn the key control structures — if statements and looping structures. With increased control comes increased opportunity for error, so you also learn how to manage problems in your code.

Making Choices with if

Sometimes you'll need your code to make decisions. For example, if somebody famous typed their name in your website, you might want to create a custom greeting for them. (I know this is a goofy example, but stay with me.) Take a look at the ifElse.html site in Figures 3-1 and 3-2.

9781118289389-fg2001.tif

Figure 3-1: Tim Berners-Lee gets a special greeting.

9781118289389-fg2002.tif

Figure 3-2: Apparently, this guy isn't famous enough.

This program (and the next few) uses a basic HTML set up to take information from a text field, respond to a button click, and print output in a designated area. Here's the HTML part of the code:

<body>
<h1>If Demo</h1>

<form action = "">
<fieldset>
<label id = "lblOutput">Please enter your name</label>
<input type = "text"
id = "txtInput"
value = "Tim Berners-Lee" />
<button type = "button"
onclick = "checkName()">
click me
</button>
</fieldset>
</form>
</body>
</html>

As you can see, the program looks at the input in the text box and changes behavior based on the value of the text field. Here's the checkName() function called in ifElse.html:

function checkName(){
// from ifElse.html
lblOutput = document.getElementById("lblOutput");
txtInput = document.getElementById("txtInput");

userName = txtInput.value;
if (userName == "Tim Berners-Lee"){
lblOutput.innerHTML = "Thanks for inventing HTML!";
} else {
lblOutput.innerHTML = "Do I know you?";
} // end if
} // end function

Changing the greeting with if

This code uses an important idea called a condition inside a construct called an if statement. Here's how to do it:

1. Set up the web page as usual.

The HTML code has elements called lblOutput and txtInput. It also has a button that calls checkName() when it is clicked.

2. Create variables for important page elements.

You're getting data from txtInput and changing the HTML code in lblOutput, so create variables for these two elements.

3. Get userName from txtInput.

Use the txtInput.value trick to get the value of the input element called txtInput and place it in the variable userName.

4. Set up a condition.

The key to this program is a special element called a condition — an expression that can be evaluated as true or false. Conditions are often (as in this case) comparisons. Note that the double equals sign (==) is used to represent equality. In this example, I'm asking whether theuserName variable equals the value “Tim Berners-Lee”.

5. Place the condition in an if structure.

The if statement is one of a number of programming constructs which use conditions. It contains the keyword if followed by a condition (in parentheses). If the condition is true, all of the code in the following set of braces is executed.

6. Write code to execute if the condition is true.

Create a set of squiggly braces after the condition. Any code inside these braces will execute if the condition is true. Be sure to indent your code, and use the right squiggle brace (}) to end the block of code. In this example, I give a special greeting to Tim Berners-Lee (because he isjust that awesome).

7. Build an else clause.

You can build an if statement with a single code block, but often you want the code to do something else if the condition was false. Use the else construct to indicate you will have a second code block that will execute only if the condition is false.

8. Write the code to happen when the condition is false.

The code block following the else clause will execute only if the condition is false. In this particular example, I have a greeting for everyone except Berners-Lee.

The different flavors of if

If statements are extremely powerful, and there are a number of variations. You can actually have one, two, or any number of branches. You can write code like this:

if (userName == "Tim Berners-Lee"){
lblOutput.innerHTML = "Thanks for inventing HTML"
} // end if

With this structure, the greeting will occur if userName is “Tim Berners-Lee” and nothing will happen if the userName is anything else. You can also use the if-else structure (this is the form used in the actual code):

if (userName == "Tim Berners-Lee"){
lblOutput.innerHTML = "Thanks for inventing HTML!";
} else {
lblOutput.innerHTML = "Do I know you?";
} // end if

One more alternative lets you compare as many results as you wish by adding new conditions:

if (userName == "Tim Berners-Lee"){
lblOutput.innerHTML = "Thanks for inventing HTML!";
} else if (userName == "Al Gore") {
lblOutput.innerHTML = "Thanks for inventing the Internet";
} else if (userName == "Hakon Wium Lie") {
lblOutput.innerHTML = "Thanks for inventing CSS";
} else {
lblOutput.innerHTML = "Do I know you?";
} // end if

remember.eps I don't repeat all the HTML code for these examples to save space. Please look on the book's website to see the appropriate HTML code that uses these examples. (Find out how to access this book's website in the Introduction.) You'll find if.html, ifElse.html, and ifElseIf.html available on the site. Be sure to view the source to see how the HTML and the JavaScript code interact. Also, review Chapter 2 of this mini-book if you want to remember how to have JavaScript code interact directly with the web page.

Conditional operators

The == operator checks to see if two values are identical, but JavaScript supports a number of other operators as well:

Operator

Meaning

a == b

a is equal to b

a < b

a is less than b

a > b

a is greater than b

a <= b

a is less than or equal to b

a >= b

a is greater than or equal to b

a != b

a is not equal to b

technicalstuff.eps If you're coming from another programming language like Java, C++, or PHP you might wonder how string comparisons work because they require different operators in these languages. JavaScript uses exactly the same comparison operators for types of data, so there's no need to learn different operators. Yeah, JavaScript!

Nesting your if statements

There are a few other variations of the if structure you'll sometimes run across. One variation is the nested if statement. This simply means you can put if statements inside each other for more complex options. For example, look at the following code:

function checkTemp(){
//from nestedIf.html
var temp = prompt("What temperature is it outside?");
temp = parseInt(temp);

if (temp < 60){
//less than 60
if (temp < 32){
//less than 32
alert("It's freezing!");
} else {
//between 32 and 60
alert("It's cold.");
} // end 'freezing if'
} else {
//We're over 60
if (temp < 75){
//between 60 and 75
alert("It's cool.");
} else {
//temp is higher than 75
if (temp > 90){
//over 90
alert("It's really hot.");
} else {
//between 75 and 90
alert("It's warm.");
} // end 'over 90' if
} // end 'over 75' if
} // end 'over 60' if
} // end function

This code looks complicated, but it really isn't. It simply takes in a temperature and looks for a range of values. Here's what's happening:

1. Get a temperature value from the user.

Ask the user for a temperature. I'm using the simple prompt statement here, but you could also grab the value from a form field. See Chapter 2 of this mini-book if you need help on that process.

2. Convert the temperature to a numeric type.

Recall that computers are fussy about data types, and sometimes you need to nudge a variable to the right type. The parseInt() function forces any value into an integer, which is perfect for our needs.

3. Use an if statement to chop the possibilities in half.

The outer (most encompassing) if statement separates all the cooler temperatures from the warmer ones.

4. Use an inner if statement to clarify more if needed.

Within the cool (less than 60 degree) temperatures, you might want to know if it's cold or below freezing, so place a second condition to determine the temperatures.

5. The upper bound is determined by the outer if statement.

The first else clause in the code is triggered when the temperature is between 32 and 60 degrees because it's inside two if statements: temp < 60 is true, and temp < 32 is false, so the temperature is between 32 and 60 degrees.

6. Indentation and comments are not optional.

As the code becomes more complex, indentation and comment characters become more critical. Make sure your indentation accurately reflects the beginning and end of each if statement, and the code is clearly commented so you know what will happen (or what you expect will happen — the truth may be different).

7. You can nest as deeply as you wish.

As you can see in this structure, there are three different possibilities for temperatures higher than 60 degrees. Simply add more if statements to get the behavior you wish.

8. Test your code.

When you build this kind of structure, you need to run your program several times to ensure it does what you expect.

Making decisions with switch

JavaScript, like a number of languages, supports another decision-making structure called switch. This is a useful alternative when you have a number of discrete values you want to compare against a single variable. Take a look at this variation of the name program from earlier in this chapter:

function checkName(){
//from switch.html
var name = prompt("What is your name?");

switch(name){
case "Bill Gates":
alert("Thanks for MS Bob!");
break;
case "Steve Jobs":
alert("The Newton is awesome!");
break;
default:
alert("do I know you?");
} // end
} // end checkName

The switch code is similar to an if-elseif structure in its behavior, but it uses a different syntax:

1. Indicate a variable in the switch statement.

In the switch statement's parentheses, place a variable or other expression. The switch statement is followed by a code block encased in squiggly braces ({}).

2. Use the case statement to indicate a case.

The case statement is followed by a potential value of the variable, followed by a colon. It's up to the programmer to ensure the value type matches the variable type.

3. End each case with the break statement.

End each case with the break statement. This indicates that you're done thinking about cases, and it's time to pop out of this data structure. If you don't explicitly include the break statement, you'll get strange behavior (all the subsequent cases will evaluate true as well).

4. Define a default case to catch other behavior.

Just like you normally add a default else to an if-elseif structure to catch any unanticipated values, the default keyword traps for any values of the variable that were not explicitly caught.

Useful as the switch structure seems to be, I'm personally not a big fan of it, for the following reasons:

· There are better options: The switch behavior can be built with the if-else structure, and can often be improved by using arrays or functions. (Arrays and functions are both described in chapter 4 of this mini-book.)

· Switches are not good with inequalities: The switch structure works fine when there are discrete values to compare (like names) but are much more awkward when you're comparing a range of values (like temperatures).

· The syntax is anachronistic: The syntax of the switch statement harkens back to the C language, developed in the early days of programming. The colons and break statements combine awkwardly with the braces used elsewhere to contain code fragments.

· Use of the break keyword is discouraged: Normally the break keyword indicates you want to break the normal flow of your program. This is often used as a shortcut by sloppy programmers who can't come up with a more elegant way to write code. Because use of the breakkeyword is discouraged elsewhere in programming, it's weird to have a structure that requires its use.

· Modern languages don't even have it: A number of the newer languages (like Python) don't support switch at all, so at some point you're likely to be in a language that cannot do switch. You might as well learn alternatives now.

For these reasons, I rarely use switch in my own programming.

Managing Repetition with for Loops

Computers are well-known for repetitive behavior. It's pretty easy to get a computer to do something many times. The main way to get this behavior is to use a mechanism called a loop. The for loop is a standard kind of loop that is used when you know how often something will happen. Figure 3-3 shows the most basic form of the for loop:

9781118289389-fg2003.tif

Figure 3-3: This program counts from one to ten.

Setting up the web page

The same web page is used to demonstrate three different kinds of for loops. As usual, the HTML code sets everything up. Here's the HTML code that creates the basic framework:

<body onload = "init()">
<h1>For loops</h1>
<form action = "">
<fieldset>
<button type = "button"
onclick = "count()">
count to ten
</button>

<button type = "button"
onclick = "back()">
count backwards
</button>

<button type = "button"
onclick = "byFive()">
count by fives
</button>

</fieldset>
</form>

<div id = "output">Click a button to see some counting...</div>
</body>

While the HTML is pretty straightforward, it does have some important features:

1. The body calls an initialization function.

Often you'll want some code to happen when the page first loads. One common way to do this is to attach a function call to the onload attribute of the body element. In this example, I call the init() function as soon as the body is finished loading. The contents of the init()function will be described in the next section.

2. The page is mostly an HTML form.

The most important part of this page is the form with three buttons on it. Each button calls a different JavaScript function.

3. A special div is created for output.

It's a good idea to put some default text in the div so you can see where the output should go and so you can ensure the div is actually changing when it's supposed to.

From this example, it's easy to see why it's a good idea to write the HTML first. The HTML code gives me a solid base for the program, and it also provides a good outline of what JavaScript code I'll need. Clearly this page calls for four JavaScript functions, init(), count(),back(), and byFive(). The names of all the functions are pretty self-explanatory, so it's pretty easy to see what each one is supposed to do. It's also clear that the div named output is intended as an output area. When you design the HTML page well, the JavaScript code becomes very easy to start.

Initializing the output

This program illustrates a situation that frequently comes up in JavaScript programming: All three of the main functions will refer to the same output area. It seems a waste to create a variable for output three different times. Instead, I make a single global output variable available to all functions, and attach the variable to that element once when the page loads.

In order to understand why this is necessary, it's important to discuss an idea called variable scope. Generally, variables are created inside functions. As long as the function is running, the variable still exists. However, when a function is done running, all the variables created inside that function are instantly destroyed. This prevents functions from accidentally changing the variables in other functions. Practically, it means you can think of each function as a separate program.

However, sometimes you want a variable to live in more than one function. The output variable in the forLoop.html page is a great example because all of the functions will need it. One solution is to create the variable outside any functions. Then all the functions will have access to it.

You can create the output variable without being in a function, but you can't attach it to the actual div in the web page until the web page has finished forming. The init() function is called when the body loads. Inside that function, I assign a value to the global output variable. Here's how the main JavaScript and the init() method code looks:

var output;

function init(){
output = document.getElementById("output");
} // end init

This code creates output as a global variable, and then attaches it to the output div after the page has finished loading.

Creating the basic for loop

The standard for loop counts the values between 1 and 10. The “count to ten” button triggers the count() function. Here's the code for count():

function count(){
output.innerHTML = "";
for (i = 1; i <= 10; i++){
output.innerHTML += i + "<br />";
} // end for loop
} // end count

Although the count() function clearly prints ten lines, it only has one line that modifies the output div. The main code repeats many times to create the long output.

1. You can use the output var immediately.

Because output is a global variable and it has already been created, you can use it instantly. There's no need to initialize it in the function.

2. Clear the output.

Set output.value to the empty string (“”) to clear the output. This will destroy whatever text is currently in the div.

3. Start a for loop.

The for loop is a special loop used to repeat something a certain number of times. For loops have three components: initialization, comparison, and update.

4. Initialize your counting variable.

A for loop works by changing the value of an integer many times. The first part of a for loop initializes this variable (often called i) to a starting value (usually 0 or 1).

5. Specify a condition for staying in the loop.

The second part of a for statement is a condition. As long as the condition is true, the loop will continue. As soon as the condition is evaluated as false, the loop will exit.

6. Change the variable.

The third part of a for statement somehow changes the counting variable. The most common way to change the variable is to add one to it. The i++ syntax is a shortcut for “add one to i.”

7. Build a code block for repeated code.

Use braces and indentation to indicate which code repeats. All code inside the braces repeats.

8. Inside the loop, write to the output.

On each iteration of the loop, add the current value of i to the output div's innerHTML. Also add a break (<br />) to make the output look better. When you add to an innerHTML property, you're writing HTML code, so if you want the output to occur on different lines, you need to write the HTML to make this happen. (See the section “Introducing shortcut operators” in this chapter for an explanation of the += statement.)

9. Close the loop.

Don't forget to end the loop, or your program will not run correctly.

Introducing shortcut operators

You might have noticed a couple of new operators in the code for forLoops.html. These are some shortcut tools that allow you to express common ideas more compactly. For example, consider the following code:

i = i + 1;

This means, “Add one to i, and store the result back in i.” It's a pretty standard statement, even if it does drive algebra teachers bananas. The statement is so common that it is often abbreviated, like this:

i += 1;

This statement means exactly the same as the last one; add one to i. You can use this to add any amount to the variable i.Because the + sign is used to concatenate (combine) strings, you can use the += shortcut with string manipulation, so consider this variation:

var userName = "Andy";
userName += ", Benevolent Dictator for Life";

The second statement appends my official (I wish) title to the end of my name.

technicalstuff.eps You can also use the -= operator to subtract from a variable. It's even possible to use *= and /=, but they are not commonly used.

Moving back to numbers — because adding one is extremely common, there's another shortcut that's even more brief:

i++;

This statement also means, “Add one to i.” In the standard for loop, I use that variation because it's very easy.

technicalstuff.eps When programmers decided to make a new variation of C, they called the new language C++. Get it? It's one better than C! Those guys are a hoot!

Counting backwards

After you understand basic for loops, it's not difficult to make a loop that counts backwards. Here's the back() function (called by the Count Backwards button):

function back(){
output.innerHTML = "";
for (i = 10; i > 0; i--){
output.innerHTML += i + "<br />";
} // end for loop
} // end back

When the user activates this function, she gets the result shown in Figure 3-4.

9781118289389-fg2004.tif

Figure 3-4: Now the page counts backwards.

This code is almost exactly like the first loop, but look carefully at how the loop is created:

1. Initialize i to a high value.

This time I want to count backwards from 10 to 1, so start i with the value 10.

2. Keep going as long as i is greater than 0.

It's important to note that the logic changes here. If i is greater than 0, the loop should continue. If i becomes 0 or less, the loop exits.

3. Subtract 1 from i on each pass.

The -- operator works much like ++, but it subtracts 1 from the variable.

Counting by fives

Counting by fives (or any other value) is pretty trivial after you know how for loops work. Here's the byFive() code called by the Count by Five button:

function byFive(){
output.innerHTML = "";
for (i = 5; i <= 25; i += 5){
output.innerHTML += i + "<br />";
} // end for loop
} // end byFive

It is remarkably similar to the other looping code you've seen.

1. Initialize i to 5.

The first value I want is 5, so that is the initial value for i.

2. Continue as long as i is less than or equal to 25.

Because I want the value 25 to appear, I set the condition to be less than or equal to 25.

3. Add 5 to i on each pass.

Each time through the loop, I add 5 to i using the += operator.

The Count by Five code is shown in action in Figure 3-5.

9781118289389-fg2005.tif

Figure 3-5: Now the page counts by fives.

Understanding the Zen of for loops

For loops might seem complex, but they really aren't. The key to making a good for loop is understanding that the for statement has three parts. All three parts of the statement refer to the same variable. Sometimes the variable used in a loop is called a sentry variable. If you don't have a better name for the sentry variable, it's traditional to use i. To make a good loop, you need to know three things about the sentry variable:

· How does it start? The first part of the for loop indicates the starting value of the sentry variable. If you're counting up, you'll usually begin the sentry variable at 0 or 1. If you're counting down, you'll usually begin the sentry value with a larger number. Regardless, you have to indicate some starting value.

· How does it end? The middle part of the for loop indicates a condition. As long as the condition remains true, the loop continues. As soon as the condition is evaluated as false, the loop ends.

· How does it change? There must be some mechanism for moving the sentry from its starting position to the final position. In a for loop, this is normally some kind of addition or subtraction. Whatever you do here, you need to ensure that it's possible for the sentry to move from the starting position to the ending position, or the loop will never end.

Building while Loops

For loops are useful when you know how often a loop will continue, but sometimes you need a more flexible type of loop. The while loop is based on a simple idea. It contains a condition. When the condition is true, the loop continues; if the condition is evaluated as false, the loop exits.

Making a basic while loop

Figure 3-6 shows a dialog box asking for a password. The program keeps asking for a password until the user enters the correct password.

9781118289389-fg2006.tif

Figure 3-6: This program keeps asking for the password until the user gets it right.

Here's the HTML code used for two different while examples:

<body>
<h1>While Loop Demo</h1>
<p>The password is 'HTML5'</p>
<form action = "">
<fieldset>
<button type = "button"
onclick = "getPassword()">
guess the password
</button>

<button type = "button"
onclick = "threeTries()">
guess the password in three tries
</button>
</fieldset>
</form>
</body>

The version shown in Figure 3-6 keeps popping up a dialog box until the user gets the answer correct.

function getPassword(){
//from while.html
var correct = "HTML5";
var guess = "";
while (guess != correct){
guess = prompt("Password?");
} // end while
alert("You may proceed");
} // end getPassword

A while loop for passwords is not hard to build:

1. Store the correct password in a variable.

Variable names are important because they can make your code easier to follow. I use the names correct and guess to differentiate the two types of password. Beginners often call one of these variables password, but that can be confusing because there are actually twopasswords (the correct password and the guessed password) in play here. The best way to design variable names is to anticipate the conditions they will be used in. This function is based on the condition guess == correct. This is a really nice condition because it's really easy to determine what we're trying to figure out (whether the guess is correct). It takes some practice to anticipate variable names well, but it's a habit well worth forming.

2. Initialize the guess to an empty value.

The key variable for this loop is guess. It starts as an empty string. It's critical to initialize the key variable before the loop begins.

3. Set up the while statement.

The while statement has extremely simple syntax: the keyword while followed by a condition, followed by a block of code.

4. Build the condition.

The condition is the heart of a while loop. The condition must be constructed so the loop happens at least once (ensure this by comparing the condition to the variable initialization). When the condition is true, the loop continues. When the condition is evaluated to false, the loop will exit. This condition compares guess to correct. If guess is not equal to correct, the code will continue.

5. Write the code block.

Use braces and indentation to indicate the block of code that will be repeated in the loop. The only code in this particular loop asks the user for a password.

6. Add code to change the key variable inside the loop.

Somewhere inside the loop, you need code that changes the value of the key variable. In this example, the prompt statement changes the password. As long as the user eventually gets the right password, the loop ends.

Getting your loops to behave

warning.eps While loops can be dangerous. It's quite easy to write a while loop that works incorrectly, and these can be an exceptionally difficult kind of bug to find and fix. If a while loop is incorrectly designed, it can refuse to ever run or run forever. These endless loops are especially troubling in JavaScript because they can crash the entire browser. If a JavaScript program gets into an endless loop, often the only solution is to use the operating system task manager (Ctrl+Alt+Delete on Windows) to shut down the entire browser.

The easy way to make sure your loop works is to remember that while loops need all the same features as for loops. (These ideas are built into the structure of a for loop. You're responsible for them yourself in a while loop.) If your loop doesn't work, check that you've followed these steps:

· Identify a key variable: A while loop is normally based on a condition, which is usually a comparison (although it might also be a variable or function that returns a Boolean value). In a for loop, the key variable is almost always an integer. While loops can be based on any type of variable.

· Initialize the variable before the loop: Before the loop begins, set up the initial value of the key variable to ensure the loop happens at least once. (How does the variable start?)

· Identify the condition for the loop: A while loop is based on a condition. Define the condition so the loop continues while the condition is true, and exits when the condition is evaluated to false. (How does the variable end?)

· Change the condition inside the loop: Somewhere inside the loop code, you need to have statements that will eventually make the condition false. If you forget this part, your loop will never end. (How does the variable change?)

technicalstuff.eps This example is a good example of a while loop, but a terrible way to handle security. The password is shown in the clear, and anybody could view the source code to see the correct password. There are far better ways to handle security, but this is the cleanest example of awhile loop I could think of.

Managing more complex loops

It won't take long before you find situations where the standard for or while loops do not seem adequate. For example, consider the password example again. This time, you want to ask for a password until the user gets the password correct or guesses incorrectly three times. Think about how you would build that code. There are a number of ways to do it, but here's the cleanest approach:

function threeTries(){
//continues until user is correct or has three
//incorrect guesses
//from while.html

var correct = "HTML5";
var guess = "";
var keepGoing = true;
var tries = 0;

while (keepGoing){
guess = prompt("Password?");
if (guess == correct){
alert("You may proceed");
keepGoing = false;
} else {
tries++;
if (tries >= 3){
alert("Too many tries. Launching missiles...");
keepGoing = false;
} // end if
} // end if
} // end while
} // end threetries

This code is a little more complex, but it uses a nice technique to greatly simplify loops:

1. Initialize correct and guess.

As in the previous example, initialize the correct and guess passwords.

2. Build a counter to indicate the number of tries.

The tries variable will count how many attempts have been made.

3. Build a Boolean sentry variable.

The keepGoing variable is special. Its entire job is to indicate whether the loop should continue or not. It is a Boolean variable, meaning it will only contain the values true or false.

4. Use keepGoing as the condition.

A condition doesn't have to be a comparison. It just has to be true or false. Use the Boolean variable as the condition! As long as keepGoing has the value true, the loop will continue. Any time you want to exit the loop, set keepGoing to false.

5. Ask for the password.

You still need the password, so get this information from the user.

6. Check to see if the password is correct.

Use an if statement to see if the password is correct.

7. If the password is correct, provide feedback to the user and set keepGoing to false.

The next time the while statement is executed, the loop ends. (Remember, you want the loop to end when the password is correct.)

8. If the password is incorrect, if the ( guess == correct)condition is false, that means the user did not get the password right.

In this case, add one to the number of tries.

9. Check the number of tries.

Build another if statement to check the number of tries.

10. If it's been three turns, provide feedback (threatening global annihilation is always fun) and set keepGoing to false.

The basic idea of this strategy is quite straightforward: Create a special Boolean variable with the singular job of indicating whether the loop continues. Any time you want the loop to exit, change the value of that variable.

tip.eps If you change most of your while loops to this format (using a Boolean variable as the condition), you'll generally eliminate most while loop issues. When your code gets complicated, it gets tempting to use and (&&) and or (||) operators to make more complex conditions. These Boolean operators are very confusing for beginners and are generally not necessary. (My rule of thumb is this: If you can explain DeMorgan's law, you can use Boolean operators in your conditions.) Most beginners (like me, and I've been doing this for thirty years) make their loops way too complicated. Using a Boolean variable in your loop can eliminate the need for Boolean operators and solve a lot of logic problems.

Managing Errors with a Debugger

By the time you're writing loops and conditions, things can go pretty badly in your code. Sometimes it's very hard to tell what exactly is going on. Fortunately, modern browsers have some nice tools that help you look at your code more carefully.

A debugger is a special tool that allows you to run a program in “slow motion,” moving one line at a time so you can see exactly what is happening. Google Chrome has a built-in debugger, so I begin with that one.

To see how a debugger works, follow these steps.

1. Load a page into Chrome.

You can add a debugger to most browsers, but Chrome has one built in, so start with that one. I'm loading the forLoops.html page because loops are a common source of bugs.

2. Open the Developer Tools window.

If you right-click anywhere on the page and choose Inspect Element (or press the F12 key), you'll get a wonderful debugging tool that looks like Figure 3-7.

3. Inspect the page with the Elements tab.

The default tab shows you the page in an outline view, letting you see the structure of your page. If you click any element in the outline, you can see what styles are associated with that element. The actual element is also highlighted on the main page so you can see exactly where everything is. This can be very useful for checking your HTML and CSS.

4. Move to the Sources tab.

The Developer Tools window has a separate tab for working with JavaScript code. Select the Sources tab to see your entire code at once. There's a small menu button that lets you select from all the source pages your program uses. If your page pulls in external JavaScript files, you'll be able to select them here as well. (Note some older versions of Chrome called this the Scripts tab.)

5. Set a breakpoint.

Typically you let the program begin at normal speed and slow down right before you get to a trouble spot. In this case, I'm interested in the count() function, so click on the first line (16) of that function in the code window. (It's more reliable to click on the first line of the function than the line that declares it, so use line 16 instead of line 15.) Click the line number of the line you want to pause, and the line number will highlight, indicating it is now a break point.

6. Refresh the page.

In the main browser, use the refresh button or F5 key to refresh the page. The page may initially be blank. That's fine — it means the program has paused when it encountered the function.

7. Your page is now running.

If you look back over the main page, you should see it is now up and running. Nothing is happening yet because you haven't activated any of the buttons.

8. Click the Count button.

The Count button should activate the code in the count function. Click this button to see if that happens.

9. Code should now be paused on line 17.

Back in the code window, line 17 is now highlighted. That means the browser is paused, and when you activate the step button, the highlighted code will happen.

10. Step into the next line.

In the Developer Tool window is a series of buttons on top of the right column. Step into the next line looks like a down arrow with a dot under it. You can also use the F11 key to activate the command.

11. Step a few times.

Use the F11 key or the step button to step forward a few times. Watch how the highlight moves around so you can actually see the loop happening. This is very useful when your code is not behaving properly because it allows you to see exactly how the processor is moving through your code.

12. Hover over the variable i in your code.

When you are in debug mode, you can hover the mouse over any variable in the code window and you'll see what the current value of that variable is. Often when your code is performing badly, it's because a variable isn't doing what you think it is.

13. Add a watch expression to simplify looking at variables.

If you think the loop is not behaving, you can add a watch expression to make debugging easier. Right under the step buttons you'll see a tab called Watch Expressions. Click the plus sign to add a new expression. Type in i and enter.

14. Continue stepping through the code.

Now you can continue to step through the code and see what is happening to the variable. This is incredibly useful when your code is not performing like you want it to.

9781118289389-fg2007.tif

Figure 3-7: The Chrome debugger makes it easy to figure out what's happening.

tip.eps I personally think the debugger built into Chrome is one of the best out there, but it's not the only choice. If you're using Firefox, the excellent Firebug extension adds the same functionality to Firefox (http://getfirebug.com/). Safari has a similar Web Inspector tool built in, and even IE finally has a decent debugger called F12. All work in roughly the same way. Usually, however, a JavaScript error will crash on all browsers, so pick one you like for initial testing, and then use other browser-specific tools only when necessary.

Debugging with the interactive console

The Developer Tools window has another really wonderful tool called the console. I introduced it briefly in Chapter 2 of this mini-book, but there's much more you can do with this wonderful tool. Try this exercise to see some of the great ways you can use the console:

1. Begin with the forLoops.html page.

You can debug any page, but forLoops.html is especially helpful for debugging.

2. Place a breakpoint.

For this demonstration, put a breakpoint in the count() function (line 16 if you're using my version of the code).

3. Step through a few lines.

Use the step button or F11 key to step through a few lines of code.

4. Switch to the console tab.

The Console tab switches to console mode. This is particularly interesting when the program is paused, as you can investigate and change the nature of the page in real time.

5. Change a style.

Try typing document.body.style.backgroundColor = lightGreen in the console. This modifies the background color of the page in real time. This is fun but not seriously useful.

6. Examine the document.body.

Type document.body in the console and press Enter. You'll see plenty of information about the body. Document.body is actually a JavaScript variable containing the current document body. It's very powerful and allows you to understand a lot about what's going on.

7. Examine the body's innerHTML.

Like any HTML element, document.body has an innerHTML property. You can examine this in the console: document.body.innerHTML.

8. Look at the variable i.

You can examine the current value of any variable as long as that variable currently has a meaning. Type i (then press enter) to see the current value of the variable i. If the count() function isn't currently running, you may get a strange value here.

9. Check the type of i.

As you may recall from Chapter 2 of this minibook, all variables have a specific type defined by JavaScript, and sometimes that data type is not what you expected. You can ask the browser what type of data any variable contains: typeof(i) returns “number.” You may also see “string” or “object.”

10. See if your output variable is defined correctly.

Like many interactive programs, this page has a div called div that contains the output. If this is not defined correctly, it won't work. Try output in the console to see if the output variable is correctly defined and in scope. You can view the contents of output withoutput.innerHTML, or you can even change the value of output like this: output.innerHTML = “Hi Mom!”.

11. Check your functions.

You can check to see if the functions are what you think they are in the console. Try typing count (with no parentheses) to see the contents of count.

12. Print to the console from your programs.

You can even have your programs print information to the console. Use the code console.log(“hi there”) anywhere in your code to have the code print a value to the console. Normally you'll do this only when your code is not functioning properly to see what's going on. You might use something like this: console.log(“current value of i” + i). The user typically doesn't know there is a console, so she won't see any results of console.log(). You should remove all calls to console.log() before releasing the final version of your code.

The console was not available in earlier browser versions, so it isn't always taught as a part of JavaScript programming. Now that it's a commonly available tool, you should definitely consider using it.

Debugging strategies

It's a fact of life — when you write code, you will have bugs. Every programmer needs to know how to diagnose and fix code when it goes wrong.

The first thing to understand is that crashes and bugs are not the same. A crash is a problem with your code that prevents the program from running at all. These sound bad, but they're actually easier to resolve than bugs, which are caused by technically correct code doing the wrong thing.

Resolving syntax errors

The most common type of error is a crash or syntax error, usually meaning you misspelled a command or used a function incorrectly. From the user's point of view, browsers don't usually tell you directly when a syntax error occurs, but simply sit there and pout. The best way to discover what's going wrong is to call up the debugging console. As soon as you discover a page not acting correctly, go to the debugging console and look at the Console tab. You'll see error messages there, and you can often click on an error message to see the problem. As an example, take a look at the following code from syntaxError.html:

function getPassword(){
var correct "HTML5";
var guess = "";
while (guess != correct){
guess = prompt("Password?");
} // end while
alert("You may proceed");
} // end getPassword

This code might look just like the getPassword() function from while.html, but I introduced a subtle error that's difficult to find with the naked eye. Run the program in your browser, click the Guess the Password button, and the browser will seem to do nothing but glare at you insolently. However, if you activate the Debugging console, you'll realize it's telling you what it thinks is wrong. Figure 3-8 illustrates the Debugging console trying to help.

9781118289389-fg2008.tif

Figure 3-8: The debugging console has useful information here!

It would be great if the debugger told you exactly what is wrong, but normally there's a bit of detective work involved in deciphering error messages. It appears in this case that there are two errors, but they're really the same thing. Click the link to the right of the first error and you'll be taken to the Sources view with the offending line highlighted, as you see in Figure 3-9.

9781118289389-fg2009.tif

Figure 3-9: Here's where the browser thinks something went wrong.

The error messages aren't always as clear as they could be, but they are usually helpful in their own way. The error message here is “unexpected string.” That means the browser encountered a string value when it expected something else. That's somewhat helpful, but the real strategy is to know that something is probably wrong with this line, and you need to look it over carefully. At some point, you'll probably realize that line 10 should have a single equals sign. Rather than var correct “HTML5”, it should read var correct = “HTML5”. This was (as are most syntax errors) a problem caused by sloppy typing. Like most syntax errors, it's kind of difficult to find (but much easier with the debugger). After you find the error, it's usually pretty easy to fix. Change the code in your editor and reload in the browser (with the F5 key) to see if your change fixes things.

Note that fixing the “unexpected string” error automatically resolves the “function not defined” error. This is pretty common because often one error cascades and causes other error messages. Generally you only need to worry about the topmost error on the list because resolving it may solve the other errors with no further work. (Of course, resolving one error may unmask other heretofore hidden errors, but this is less common.)

Squashing logic bugs

Syntax errors seem bad because they cause the whole program to crash, but they're actually pretty easy to resolve. There's another type of problem called logic errors that are much more troublesome. In fact, they're nearly impossible to resolve without some sort of debugging tool. However, like a syntax error, when you can find a logic error, it's usually quite easy to repair. Take a look at logicError.html to see a typical logic problem in the getPassword() function:

function getPassword(){
var correct = "HTML5";
var guess = "";
while (guess == correct){
guess = prompt("Password?");
} // end while
alert("You may proceed");
} // end getPassword

Just looking at the code, it's very difficult to see the problem. Worse, when you run the program in your browser, it won't report an error. It won't work correctly, but the code is all technically correct. Rather than telling it to do something illegal (which would result in a syntax error), I have told the program to do something that's completely legal but not logical. Logic errors are called bugs, and they're much more interesting (but subtle) to resolve than syntax errors (normally called crashes).

To resolve a logic error, there's a few steps:

1. Understand what you're trying to accomplish.

Whenever you write a program, be sure you review what you're trying to accomplish before you run the program. If you don't know what you expect, you won't know if your program got there. It's often good to write down what you expect so you'll know if you got there. (Professional programmers are usually required to list expectations before they write a single line of code.) For this example, when the user clicks the Guess the Password button, the user should get a prompt allowing them to guess the password.

2. Understand what your code did.

Run the logicError.html page yourself to see what actually happens. With a logic error, the behavior is unpredictable. A loop may never happen, it may never end, or it might sometimes work right and sometimes not. The key to finding logic errors is to predict why the code is doing what it's doing and why it's not doing what you want. In this example, when I press the Guess the Password button, the You May Proceed dialog box immediately appears, never giving me the chance to guess a password.

3. Form a hypothesis or two before looking at code.

Think about what is wrong before you look over the code. Try to describe in plain English (not technical jargon) what is going wrong. In this case, I think something is preventing the prompt from appearing. Maybe the statement causing the prompt is written incorrectly, or maybe the code is never getting there. Those are the two most likely possibilities, so they're what I'll look for. Decide this before you look at code because the moment you see code, you'll start worrying about details rather than thinking about the big picture. Logic errors are almost always about logic, and no amount of staring at code will show you logic errors, nor will a debugger spot them for you.

4. Resolve syntax errors.

Go to the console and see if there are any syntax errors. If so, resolve them. Logic errors will not appear until you've resolved all syntax errors. If your code shows no syntax errors but still doesn't work correctly, you've got a logic error.

5. Start the debugger.

Interactive debugging is incredibly helpful with logic errors. Begin with your English definitions of what you think should happen and what you know is happening. Find the function you think is the problem and set a breakpoint at that function.

6. Identify key variables or conditions.

Most logic errors are centered around a condition that's not working right, and conditions are usually based on variables. Begin by taking a careful look at the conditions that control the behavior you're worried about. In this case, I've got a loop that doesn't seem to be happening — ever. This means I should take a careful look at that loop statement and any variables used in that statement.

7. Step to your suspicious code.

If you're worried about a condition (which is very common), use the debugger tools to step to that condition, but don't run it yet. (In most debuggers, a highlighted line is about to be run.)

8. Look at the relevant variables.

Before running the condition line, think about what you think any variables used in that condition should contain. Use the Watch tools or hover over the variable names to ensure you know the current values and they're what you think they should be. In this example, I'm concerned about line 12 (while guess == correct), so I want to see what those variables contain.

9. Predict what the suspicious line should do.

If you're worried about a condition, you're generally expecting it to do something it isn't doing. In this case, the condition should trigger the prompt command on line 13 when the function is called, but it appears that we're never getting to line 13 (or we are getting there and line 13 isn't doing what we think it's doing). The goal of debugging is to identify which possible problems could be happening and isolate which of these problems are actually occurring. Make sure you know what you're looking for before you start looking for it.

10. Compare your expectations with reality.

As you step through the getPassword() function in the debugger (with the step into button or F11 key), you might see the problem. The while loop begun in line 12 never executes, meaning line 13 never happens, but it should always happen on the first pass. Now you know exactly what the program is doing, but you don't know why yet.

11. Think about your logic.

Logic errors aren't about getting the commands right (those are syntax errors). Logic errors are about telling the computer to do the wrong thing. Think hard about the logic you've applied here. In this case, it appears my condition is backwards. You told the computer to continue looping as long as the guess is correct. You probably meant to continue as long as the guess is incorrect. The guess starts out incorrect because of the way you (appropriately) initialized both variables. Thus the condition is automatically skipped and the prompt never happens.

12. Fix it.

Fixing code is easy when you know what's wrong. In this case, my condition was legal but illogical. Replace guess == correct with guess != correct and your code will work correctly.

Don't worry if you find debugging difficult. Programming is both an art and a science, and debugging logic errors falls much more along the art side of the equation. It does get much easier with practice and experience.


Couldn't we make this automatic?

If a debugger can find syntax errors, wouldn't it be awesome if debuggers could find logic errors too? This issue turns out to be one of the big unsolved problems of computer science. Researchers are still trying to discover a technique for mathematically determining whether a program is logically correct without having to run it, and such efforts are called proofs of program correctness. If you study formal computer science, you'll encounter these problems as part of a programming languages class. Who knows? You might be the person who solves this problem, and makes programming easier for everybody!