Decisions

Webpages and stylesheets are rendered from top to bottom. Scripts (and most other programs) are not. Since scripting languages are programming languages, they can decide whether or not to run some code, repeat the same set of instructions, reference different parts of themselves, and even reference each other, if you've got more than one script.

The decisions are done by if statements and switch statements, the repetition done by loops, and referencing is done using procedures. I'll get to the last two later.

If Statements

An if statement contains code that will run only if a condition is true, if/else statements have one set of instructions for a condition proving true, a second for the condition proving false, and if/else if/else have several conditions to be checked in order.

Special Characters

There are a few special characters to deal with when working with if statements.

()
Within the parenthesis, you set conditions for the if statements.
{}
This sets a series of instructions apart into their own group.

Comparison Operators

The following are some comparison operators. Some of them work with numbers only, but a few can work with strings as well.

!
This means not. In the last chapter, I mentioned the true keyword. Therefore, !true would mean not true, which is the same as false.
==
Is equal to. Note that there are two equal signs; this keeps the equality operator from being confused with =, which assigns values, and may be used in this context. This works on strings as well as numbers.
===
Is exactly equal to. In this case, if I'm comparing a variable to the number five, not only must the variable contain the value 5, but the value must be a number. If it's a string, this will prove false.
!=
Is not equal to. Note the use of !. This works on strings as well as numbers.
<
<=
The first term means Is less than. The second term means Is less than or equal to.
>
>=
The first term means Is greater than. The second term means Is geater than or equal to.

Automatically False

The following values that will prove automatically false:

This is a good thing: false is a boolean value (the only other possible value is true), an empty string can't have any matching, replacements, or any other such string manipulation done with it, and undefined and null both mean there's nothing here. The false value most likely to cause trouble is 0. That's where === comes in handy, since it will check the value as a number, instead of a potential false value.

Simple If Statements

The simplest type of decision is the if statement, which has code that is executed if the condition proves true. Whether or not it is run, the program goes on its merry way to the rest of itself.

For example, let's take the following code:

Boolean Demonstration
var bool = true;
var foo = "False";
if(bool){
foo = "True";
}
document.getElementById("JS").firstChild.data = foo;

In the above code, the variable bool is set to true. Note that I don't have quotation marks around true in the code. This is because true is not a string, it is a boolean value, and remember I said that a boolean value is always either true or false.

Okay, now that that's out of the way, let's move on.

The variable foo is declared and contains the word False. Then there is an if statement with a condition that says if the variable bool holds a value that is considered true, execute this if statement's code. After that, write the resulting text to the text node with a parent element node with an ID of JS.

The variable bool does indeed hold the value true, so the code within the if statement is executed. The code says, Change the value of foo to the word True. And the value is indeed changed:

When bool = true

But say bool was false?

Boolean Demonstration
var bool = false;
var foo = "False";
if(bool){
foo = "True";
}

In this case, the condition is false, so the if statement's code is completely skipped, and the value of foo remains unchanged:

When bool = false

if/else Statements

An if/else statement allows for another set of instructions to be run if the if statement's condition is false. For example, let's set up an if statement that states whether or not foo equals 5.

A Basic If/Else Statement
var answer; // No need for a value yet
if(foo == 5){ // Note the comparison operator!
answer = "Foo equals 5";
} else {
answer = "Foo does not equal 5";
}
document.getElementById("JS").firstChild.data = answer;

You don't have to have else right where it is; you can have it on its own line, but I did admit that this book is rife with my own coding habits.

If foo does equal 5, answer will be given the value Foo equals 5. But if foo was any other number, then the else part would be run instead, and answer would be given the value Foo does not equal 5. This is demonstrated below:

var foo = 5;
When foo = 5;
var foo = 3;
When foo = 3;
var foo = 6;
When foo = 6;

If/Else Shorthand

There is a shorthand way of writing if/else statements, and it's common to many coding languages. It is:

If/Else Shorthand Syntax
([Condition])?[If true]:[If false];

Rewriting the above code using this shorthand gives you:

If/Else Shorthand Example
var answer = (foo == 5)?"Foo equals 5":"Foo does not equal 5";
document.getElementById("JS").firstChild.data = answer;

From seven lines of code to only two! Below, I cut out yet another line:

If/Else Shorthand Example 2
document.getElementById("JS").firstChild.data = (foo == 5)?"Foo equals 5":"Foo does not equal 5";

Do note, however, this only works with if/else statements!

Advantages of Shorthand Statements

Because of the way this statement is constructed, it can be used in more places than the usual if/else statement. Below are examples of these places.

If/Else Shorthand Used In Various Contexts
Concatenation (Requires Statement To Be Enclosed In Parentheses)
document.getElementById("JS").firstChild.data = "Foo " + ((foo == 5)?"equals":"does not equal") + " 5";
To Select An Array Index
var equality = Array('equals', 'does not equal');
document.getElementById("JS").firstChild.data = "Foo " + equality[(foo == 5)?0:1] + " 5";

I have used both usages from time to time.

Disadvantages of Shorthand Statements

The shorthand if statement's strength is its strictly-defined usage. The shorthand if statement's weakness is its strictly-defined usage. In short, it can only pass one value or another in a single context, whether it is assigning a value to a variable, or selecting an array element, or choosing what to concatenate to a string, or something else. It cannot choose between variables to assign values to, nor can it choose which array to get an index from, nor can it choose which string to concatenate to. If that's what you want to do, use a full if statement.

If, Else If, Else

The else if pair of keywords allows you to add more options to an if/else statement. You can only have one if statement and one else statement, but infinite else if statements. What it means is, if the preceding conditions are not true, what about this one?

Below is the code for a script that checks to see if foo is higher or lower than 5

If/Else If/Else Example
var answer = "Foo is "; // The beginning two words will be identical in every case, so I may as well put it here.
if(foo < 5){
answer += "less than ";
} else if(foo > 5){
answer += "greater than ";
} else {
answer += "equal to ";
}
answer +="5"; // The last number is identical in every case, so I may as well put it here.

The above code first checks to see if foo is less than 5. If foo equals 3, then yes, it is less than 5. If it was greater than or equal to 5, then the first condition would be false.

The code then checks to see if foo is greater than 5. If foo was equal to 3, the first condition would have been satisfied, and the rest of the instructions ignored. However, if foo was equal to 6, then the second condition would be satisfied.

If foo is neither greater nor less than 5, then logic dictates that it would be exactly equal to 5, and thus the code of the else portion of this statement.

var foo = 3;
When foo = 3;
var foo = 6;
When foo = 6;
var foo = 5;
When foo = 5;

Nested If Statements

You can actually nest if statements, especially when the conditions on the inner if statement depend on those of the outer if statement. For example with our code, let's imagine that, instead of a numerical value, foo might contain the string Something else instead. If not, then we can check its numerical value. First, we check to see if the string's value can be converted into a number at all. If it can't be converted to a number, then the function isNaN will return true.

Beginning A Nested If Statement
var answer = "Foo is "; // The beginning two words will be identical in every case, so I may as well put it here.
if(isNaN(foo)){
answer += "a string of text.";
} else {
// Nothing here just yet.
}

If isNaN returns true (in other words, if foo is not a number), then Foo is a string of text is the script's final answer. If isNaN returns false (literally, if foo is not not a number), then the else statement comes into play:

A Nested If Statement
var answer = "Foo is "; // The beginning two words will be identical in every case, so I may as well put it here.
if(isNaN(foo)){
answer += "a string of text.";
} else {
if(foo < 5){
answer += "less than ";
} else if(foo > 5){
answer += "greater than ";
} else {
answer += "equal to ";
}
answer += "5"; // The last number is identical in every numerical case, so I may as well put it here.
}

The results are:

var foo = "Something else";
When foo = 'Something else';
var foo = 3;
When foo = 3;
var foo = 6;
When foo = 6;
var foo = 5;
When foo = 5;

If Statements and Strings

With the methods I explained in the chapter on text, you can see how easily these methods can be converted into conditions for if statements. Below is a group of conditions that take advantage of indexOf, lastIndexOf, search, and match.

The first three use a simple mathematical comparison: any returned value greater than -1 means that the substring was found, and thus this returns true.

Seeking Out Indices Of Substrings
if(string.indexOf(substr) > -1){...}
if(string.lastIndexOf(substr) > -1){...}
if(string.search(substr) > -1){...}

The match method, however, returns null if it doesn't find a match and, as I said at the beginning of the chapter, null is always considered to be false. That makes this method a bit simpler.

Matching A Substring
if(string.match(substr)){...}

Multiple Conditions

There are times when more than one condition decides whether or not code is run. There are two variants of this: either a) all conditions must be met, or b) at least one condition must be met. Variant a can be emulated with nested if statements, while variant b requires at least three if statements in succession. In the examples below, foo must equal five if a statement is to prove true, while bar must equal 5.

Both Conditions Must Prove True
if(foo == "five"){
if(bar == 5){
/* Code Goes Here */
}
}

If any coding goes inside the outer if statement but outside the inner statement, this is what you would use. But if all your coding is inside the inner if statement, the way to shorten it us to use the and operator: &&. Using this operator, you can put both conditions into a single, larger condition, but you must write both conditions so they can stand on their own.

Both Conditions Must Prove True
if(foo == "five" && bar == 5){
/* Code Goes Here */
}

This reduces the coding by two lines, and makes the statement similar to follow. You can use this with as many conditions as you like.

It gets somewhat more complex when you want either condition to prove true.

Either Condition Must Prove True
var boolean = false;
if(foo == "five"){boolean = true;}
if(bar == 5){boolean = true;}
if(boolean){
/* Code Goes Here */
}

A slightly shorter way to do this would be assigning a condition to a variable. Yes, you can do that, and the result will be a boolean value.

Assigning A Condition To A Variable
var boolean = (foo == "five");
if(!boolean){boolean = (bar == 5);}
if(boolean){
/* Code Goes Here */
}

If there's no other pertinent coding, this can be shrunk using the or operator: ||.

Either Condition Must Prove True
if(foo == "five" || bar == 5){
/* Code Goes Here */
}

I mentioned back in Math that operations inside parentheses are always done first, and the results are applied to operations outside the parentheses. Conditions are no different. A limitation of the or operator is that it cannot distinguish between only one or more than one condition to be true. If you wanted a condition that would prove true if foo was five or bar was 5, but not both, you'd have to get a little more complex and use two and operators, an or operator, and a total of four conditions (since we're working with two variables).

A More Complex Condition
if(
(foo == "five" && bar != 5) If "foo" equals "five" and bar does NOT equal "5"
|| Or...
(foo != "five" && bar == 5) If "foo" does NOT equal "five" and bar equals "5"
){
// Code goes here
}

As they're in parentheses, the two statements with the and operators are processed first. Because of the way they're set up, if foo equals five and bar equals 5, then in both conditions, something will prove false and—because of the and statements—both subconditions will prove false, causing the condition as a whole to prove false. The same is true if neither foo equals five or bar equals 5.

But if only foo equals five or bar equals 5 (meaning the other variable equals something else, say bar equals 42 or foo equals Mr. Billion), then one of those subconditions will prove true: either foo will equal five and bar will not equal 5 or vice versa. Because of the or operator, the condition as a whole will prove true.

The Order Of Subconditions

The order of subconditions is important: aside from what are in parentheses, subconditions are processed in the order they occur. This comes in handy, and I'll show you why.

In Dynamic Behavior And Scripting, I mentioned that the src attribute, used by the script element to refer to an external script file, would cause the bowser not to process anything in the script element itself. I also said you could use this to your advantage.

I have used this little fact to place flags, or little bits of text, to determine whether or not I wanted a section of script to be run. Say I have a script that looks for the flag RandomImage in the script element that refers to the external script file.

Seeking A Flag
var scripts = document.getElementsByTagName('script');
var this_script = scripts[(scripts.length - 1)];
// The above WILL get the script element that calls this external script file. Any further script elements have not been processed yet.
if(this_script.firstChild.data.match("RandomImage"){
// Code goes here
}

The above code will work fine—so long as the script element has a first child node. It is entirely possible that the script element would look like this:

Look Ma! No Child Node!
<script type="text/javascript" src="./script.js"></script>

The script will encounter an error because there is no firstChild node to extract data from and match. The problem can be avoided by first checking to see if there is such a node at all:

Seeking A Text Node, Then A Flag
var scripts = document.getElementsByTagName('script');
var this_script = scripts[(scripts.length - 1)];
// The above WILL get the script element that calls this external script file. Any further script elements have not been processed yet.
if(this_script.firstChild && this_script.firstChild.data.match("RandomImage"){
// Code goes here
}

Because it uses the && operator, if the first condition proves false, the script won't bother with the rest of it; in this case, that means if this_script.firstChild does not exist, the script will not try to seek the RandomImage flag. If the two subconditions were reversed, the script would attempt to make the match, then check to see if the script element might have a child node. Obviously, this is no better than not doing a child node check at all.

If Statements And Regexes

As explained in Regular Expressions, the test returns true or false, making them ideal for use in if statements. I also mentioned in the same chapter that false matches could occur. With this knowledge, I am going to show how to filter out possible false matches. The first regex I'm going to use is intended to match a valid Canadian phone numbers. The other regexes are intended to filter out possible errors by matching those.

Phone Number Regexes
Valid Phone Number
/((1-)?[2-9][0-9]{2}-)?[2-9][0-9]{2}-[0-9]{4}/
Invalid Phone Number—Area Code Starts With 0 Or 1
/[01][0-9]{2}-[0-9]{3}-/
Invalid Phone Number—Three Three-Number Sequences
/([0-9]{3}-){3}/
Using The Above In A Complex Condition To Filter Out False Matche
if(
(/((1-)?[2-9][0-9]{2}-)?[2-9][0-9]{2}-[0-9]{4}/.test(phone_number)) &&
!( // The "!", the grouping with parentheses, and the "||" used below mean that ALL the following conditions must prove FALSE for the condition as a whole to prove TRUE.
(/[01][0-9]{2}-[0-9]{3}-/.test(phone_number) || //The first expression that would match an invalid phone number.
(/([0-9]{3}-){3}/.test(phone_number) || //The other expression that would match an invalid phone number.
)
){
//Coding Goes Here
}

In this way, I exclude anything I don't want to match.

The Confirm Dialogue Box

Confirm popups are sort of like alert boxes, but they allow you to make a choice. Clicking Okay returns true, while clicking Cancel will return false. Below, I've created a short demonstration of the confirm box.

The Confirm Dialogue
if(confirm("Do you want to click 'OK'?")){
alert("You clicked 'OK'.");
} else {
alert("You clicked 'Cancel'.");
}

Switch Statements

A switch statement works a bit like an if/else if/else statement, but it looks for an exact match rather than a broad generalization. For example, you can't use a switch statement to see if foo is higher or lower than 5, but you can use a switch statement to see if a number is 4, 5, or 6.

For example, say you've written a script, and part of it needs to see what type of node foo is. An if/else if/else statement could work, but this suits a switch statement as well: there is a limited list of 12 node types—a list you can limit further because most are never used in HTML-oriented scripting anyway—and most of those aren't all that common in XHTML scripting either.

The syntax of a switch statement is a bit complex. First, you have to declare a switch statement with the keyword switch. The keyword is followed by the variable name in parentheses, and everything it works with after that is in curly brackets. So here is the code we set up, along with the variables we'll use:

Starting A Switch Statement
var node = document.getElementById("JS");
var foo = node.nodeType;
var text;
switch (foo){
}

I do have the alternative, of course, to write the above code like this:

Starting A Switch Statement: Alternative
var node = document.getElementById("JS");
var text;
switch (node.nodeType){
}

The above will work perfectly well.

Conditions here are known as cases, and they are shown by the keyword case. The keyword case is followed by the value to be matched, which is then followed by a colon. The node types we'll be looking for are numbers 1, 3, and 9 (element, text, and document, respectively).

Defining Cases
var node = document.getElementById("JS");
var foo = node.nodeType;
var text;
switch (foo){
case 1:
case 3:
case 9:
}

Following these is the code you want each case to execute:

Assigning Tasks To Cases
var node = document.getElementById("JS");
var foo = node.nodeType;
var text;
switch (foo){
case 1:
text = "Element node";
case 3:
text = "Text node";
case 9:
text = "Document node";
}

So lets run this:

switch error

Something doesn't make sense; node clearly refers to an element node, not a document node. The problem is, when a case is matched, the rest of the code is run as well. So, when case 1 is matched, the variable text is assigned the string Element node—then Text node and finally Document node.

To stop the rest of the coding from executing, you need the break keyword, which means leave this set of instructions; you're done.

The last keyword in a switch statement is default, which means that if no case is matched, this is what runs. It doesn't need the keyword break because it's a last resort anyway. Adding that in will give us a complete switch statement.

A Complete Switch Statement
var node = document.getElementById("JS");
var foo = node.nodeType;
var text;
switch (foo){
case 1:
text = "Element node";
break;
case 3:
text = "Text node";
break;
case 9:
text = "Document node";
break;
default:
text = "A different node";
}
document.getElementById("JS").firstChild.data = text;

Because foo refers to an element node, the result is:

String Addition

You could, of course, rewrite the entire thing like this:

The Switch Statement Rewritten As An If/Else If/Else Statement
var node = document.getElementById("JS");
var foo = node.nodeType;
var text;
if (foo == 1){
text = "Element node";
} else if(foo == 3){
text = "Text node";
} else if(foo == 9){
text = "Document node";
} else {
text = "A different node";
}
document.getElementById("JS").firstChild.data = text;

Some prefer switch statements, others swear by if/else if/else statements, and I just pity the foo who doesn't know there's a choice.

Like if statements, switch statements can work with strings as well, with each case being followed by a string you want to be matched. In the example below, I match the name given to each type of element (which are exactly what I wrote them here as), and return a description from that.

A Switch Statement Comparing Strings
var element_name = "p"
var node = document.getElementsByTagName(element_name)[0].nodeName;
var text = " ";
switch(node){
case "HTML":
text = "<html> Element"
break;
case "HEAD":
text = "<head> Element";
break;
case "BODY":
text = "<body> Element";
break;
case "P":
text = "<p> Element";
break;
case "META":
text = "<meta> Element";
break;
case "TITLE":
text = "<title> Element";
break;
case "SCRIPT":
text = "<script> Element";
break;
default:
text = "A different Element";
}
document.getElementById("JS").firstChild.data = text;

This, of course, would return <p> Element.

You may wonder why all the element names to be matched are in capital letters. Remember back in Your First Webpage, I mentioned that in HTML, case didn't matter when it came to element names? Here's an exception: so far as the HTML DOM is concerned, tag names are always in capitals. In the XHTML DOM, they're always lower-case.