Procedures

A   procedure is a chunk of coding set aside for some reason. These reasons can be:

As I mentioned back in Dynamic Scripting, there are two types of procedures: functions, which stand on their own, and methods, which are associated with objects. For example, the function Number is not associated with any object, but the method pow is associated with the Math object.

Calling a procedure means you put it to work.

Where To Put A Procedure

Before we talk about this, I want to make something very clear: you have the same number of scripts as you do script elements. Whether each script are contained in the actual element or a separate file or a mix of these; it doesn't matter. One script for each script element.

With that in mind, a procedure must be in one of two places:

Remember when I said that an element must exist before you start working with it? Scripts are no exception. This is one of the reasons that, while scripts that immediately take effect are best placed at the end of the body element, scripts consisting entirely of procedures usually go in the head element.

Creating A Procedure

A procedure always starts with the keyword function, followed by the procedure name (let's call this once P_Full_Text, referring to the fact that it gets all text from a p element.

No, there needs to be no capitalization of the name; it's just a habit of mine.

After the procedure name, you have a pair of parentheses, then a pair of braces:

The Keyword, Name, Parentheses, and Braces of A Procedure
function P_Full_Text(){}

You have to be careful with the procedure name, since if you have two procedures with the same name used by the same page, they will collide. Even if they're part of two separate external script files, if you use them with the same page there will be trouble. I know this to my frustration.

Between the braces goes whatever coding you desire: loops, if statements, calls to other procedures—even other procedures!

Remember the while loop that extracted the text of a p element and displayed it in an alert box that I demonstrated back in Loops? Let's use that in P_Full_Text:

Adding Code To A Procedure
function P_Full_Text(){
var p_child = document.getElementById("p_1").childNodes;
var p_text = "";
for(var loop = 0; loop < p_child.length; loop++){
p_text += (p_child[loop].nodeType == 3)?(p_child[loop].data):(p_child[loop].firstChild.data);
}
alert(p_text);
}

To call this particular procedure, all you need is the following code:

Calling A Procedure
P_Full_Text();

Seriously, that's it. The following result is this pop-up box.

The extracted text of the paragraph

Getting Something Out Of The Procedure

If you want the procedure to return a value (and a lot of procedures do), you need the keyword return, which says return this value.

So if I want P_Full_Text to return the value of the text, I can add this line to the end of the procedure:

How To Return A Value
return p_text;

Here's the final result (the alert function has been replaced by the code that returns the value).

Getting A Value From P_Full_Text
function P_Full_Text(){
var p_child = document.getElementById("p_1").childNodes;
var p_text = "";
for(var loop = 0; loop < p_child.length; loop++){
p_text += (p_child[loop].nodeType == 3)?(p_child[loop].data):(p_child[loop].firstChild.data);
}
return p_text;
}

When I call this procedure, it is best to call it so the value it returns is stored in a variable, or even displayed in an alert box:

Assigning The Value Returned By P_Full_Text
alert(P_Full_Text());

Remember when I mentioned that the code inside parentheses is executed before the processing outside back in Numbers and Math? This holds true for procedures as well: the call to P_Full_Text is inside a pair of parentheses (those belonging to the alert function), so it is executed first.

Returning Multiple Values

Once the procedure returns a value, it is finished and the procedure will not execute anything after it returns a value. So how can you return multiple values? Use an array to hold the variables that the procedure generates, and once that procedure returns the array, you can use coding to pick out the information you want.

Procedure Variables

A procedure can have values passed to it when it is called. For example, you've often seen getElementById("p_1") throughout my examples. In the example pages, there is a p element with the ID p_1. When I create a reference to it, I call the method getElementById. The value I pass to that procedure is the ID of the element that I want—in this case, p_1.

I haven't the foggiest idea what the method getElementById looks like, so I'll use P_Full_Text instead. And, instead of letting the procedure decide which element is referenced, I'll tell the procedure which element to reference. The procedure variable used to pass the value is named p_id.

Allowing P_Full_Text To Receive A Value
function P_Full_Text(p_id){
var p_child = document.getElementById(p_id).childNodes;
var p_text = "";
for(var loop = 0; loop < p_child.length; loop++){
p_text += (p_child[loop].nodeType == 3)?(p_child[loop].data):(p_child[loop].firstChild.data);
}
return p_text;
}

Calling the procedure and storing the value in the variable para_text I get:

Passing The Value To P_Full_Text Via A Procedure Call
alert(P_Full_Text("p_1"));

This will get me the text stored in the element with the ID p_1.

But say I wanted to add in a second variable that decides whether I want the text of an element with a specific ID, or the number of child nodes that element has. The second variable is separated from the first by a comma, and the code updated thusly (I'm using the if-else shorthand I talked about in Decisions):

Allowing P_Full_Text To Receive 2 Values
function P_Full_Text(p_id, what){
var p_child = document.getElementById(p_id).childNodes;
if(what == "text"){
var p_text = "";
for(var loop = 0; loop < p_child.length; loop++){
p_text += (p_child[loop].nodeType == 3)?(p_child[loop].data):(p_child[loop].firstChild.data);
}
}
return (what == "text")?p_text:p_child.length;
}

In the following example, the webpage created in Your First Webpage has a table added to it to show a) the number of child nodes each p element, and b) the plain text of it. Below is the code for the page along with a touch of styling, so that the table is be easier to read.

The HTML & CSS Of The Page
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="Content-Style-Type" content="text/css">
<meta http-equiv="Content-Script-Type" content="text/javascript">
<title>My First Webpage</title>
<style type="text/css">
table, th, td{
border:1px solid black;
border-collapse:collapse;
padding:5px;
}
caption{
padding:10px;
font-weight:bold;
}
</style>
<script type="text/javascript">
/* Script #1 Here */
</script>
</head>
<body>
<h1 id="h1_1" title="The traditional starting point.">Hello World</h1>
<p id="p_1"><strong id="strong_1">Welcome!</strong> This is my <em id="em_1">first</em> webpage!</p>
<p id="p_2">It's a fairly simple webpage, but it <em id="em_2">is</em> a complete webpage.</p>
<table>
<caption>Paragraph Statistics</caption>
<tr><th>Paragraph #</th><th>Child node count</th><th>Text</th></tr>
<tr><td>1</td><td id="td_1-1">&nbsp;</td><td id="td_1-2">&nbsp;</td></tr>
<tr><td>1</td><td id="td_2-1">&nbsp;</td><td id="td_2-2">&nbsp;</td></tr>
</table>
<script type="text/javascript">
/* Script #2 Here */
</script>
</body>
</html>

There's something I want you to notice: I have two script elements: one in the head element, one in the body element. This is because I want to demonstrate that separating the procedures from the scripts that call them is possible—and often advisable, since your procedures may be used over and over again, but the scripts that call them may vary widely.

Below, then, are the two scripts: one containing the procedure, one containing the procedure calls.

The JavaScript For The Page
Script #1
function P_Full_Text(p_id, what){
var p_child = document.getElementById(p_id).childNodes;
if(what == "text"){
var p_text = "";
for(var loop = 0; loop < p_child.length; loop++){
p_text += (p_child[loop].nodeType == 3)?(p_child[loop].data):(p_child[loop].firstChild.data);
}
}
return (what == "text")?p_text:p_child.length;
}
Script #2
document.getElementById("td_1-1").firstChild.data = P_Full_Text("p_1");
document.getElementById("td_1-2").firstChild.data = P_Full_Text("p_1", "text");
document.getElementById("td_2-1").firstChild.data = P_Full_Text("p_2");
document.getElementById("td_2-2").firstChild.data = P_Full_Text("p_2", "text");

The fact that the script with the procedure is in the head and therefore before any p element nodes does not cause an error because this procedure is not yet called; its code and reference have not been run yet. Since the script calls this procedure is at the bottom of the page, the nodes P_Full_Text is intended to refer to have already been processed when its code is run.

The page below displays why external script files are helpful. Look how the JavaScript expands the HTML document:

The Page With All Three Languages Stuffed Into One File
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="Content-Style-Type" content="text/css">
<meta http-equiv="Content-Script-Type" content="text/javascript">
<title>My First Webpage</title>
<style type="text/css">
table, th, td{
border:1px solid black;
border-collapse:collapse;
padding:5px;
}
caption{
padding:10px;
font-weight:bold;
}
</style>
<script type="text/javascript">
function P_Full_Text(p_id, what){
var p_child = document.getElementById(p_id).childNodes;
if(what == "text"){
var p_text = "";
for(var loop = 0; loop < p_child.length; loop++){
p_text += (p_child[loop].nodeType == 3)?(p_child[loop].data):(p_child[loop].firstChild.data);
}
}
return (what == "text")?p_text:p_child.length;
}
</script>
</head>
<body>
<h1 id="h1_1" title="The traditional starting point.">Hello World</h1>
<p id="p_1"><strong id="strong_1">Welcome!</strong> This is my <em id="em_1">first</em> webpage!</p>
<p id="p_2">It's a fairly simple webpage, but it <em id="em_2">is</em> a complete webpage.</p>
<table>
<caption>Paragraph Statistics</caption>
<tr><th>Paragraph #</th><th>Child node count</th><th>Text</th></tr>
<tr><td>1</td><td id="td_1-1">&nbsp;</td><td id="td_1-2">&nbsp;</td></tr>
<tr><td>1</td><td id="td_2-1">&nbsp;</td><td id="td_2-2">&nbsp;</td></tr>
</table>
<script type="text/javascript">
document.getElementById("td_1-1").firstChild.data = P_Full_Text("p_1");
document.getElementById("td_1-2").firstChild.data = P_Full_Text("p_1", "text");
document.getElementById("td_2-1").firstChild.data = P_Full_Text("p_2");
document.getElementById("td_2-2").firstChild.data = P_Full_Text("p_2", "text");
</script>
</body>
</html>

This following page puts both scripts and the stylesheet in separate files:

The Code Associated With The File p_stats_split.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="Content-Style-Type" content="text/css">
<meta http-equiv="Content-Script-Type" content="text/javascript">
<title>My First Webpage</title>
<link type="text/css" rel="stylesheet" href="./p_stats.css">
<script type="text/javascript" src="./p_stats_function.js"></script>
</head>
<body>
<h1 id="h1_1" title="The traditional starting point.">Hello World</h1>
<p id="p_1"><strong id="strong_1">Welcome!</strong> This is my <em id="em_1">first</em> webpage!</p>
<p id="p_2">It's a fairly simple webpage, but it <em id="em_2">is</em> a complete webpage.</p>
<table>
<caption>Paragraph Statistics</caption>
<tr><th>Paragraph #</th><th>Child node count</th><th>Text</th></tr>
<tr><td>1</td><td id="td_1-1">&nbsp;</td><td id="td_1-2">&nbsp;</td></tr>
<tr><td>1</td><td id="td_2-1">&nbsp;</td><td id="td_2-2">&nbsp;</td></tr>
</table>
<script type="text/javascript" src="./p_stats_calls.js"></script>
</body>
</html>
The Stylesheet (p_stats.css)
table, th, td{
border:1px solid black;
border-collapse:collapse;
padding:5px;
}
caption{
padding:10px;
font-weight:bold;
}
The Script With The Procedure (p_stats_function.js)
function P_Full_Text(p_id, what){
var p_child = document.getElementById(p_id).childNodes;
if(what == "text"){
var p_text = "";
for(var loop = 0; loop < p_child.length; loop++){
p_text += (p_child[loop].nodeType == 3)?(p_child[loop].data):(p_child[loop].firstChild.data);
}
}
return (what == "text")?p_text:p_child.length;
}
The Script With The Procedure Calls (p_stats_calls.js)
document.getElementById("td_1-1").firstChild.data = P_Full_Text("p_1");
document.getElementById("td_1-2").firstChild.data = P_Full_Text("p_1", "text");
document.getElementById("td_2-1").firstChild.data = P_Full_Text("p_2");
document.getElementById("td_2-2").firstChild.data = P_Full_Text("p_2", "text");

This is much easier to keep track of, since now you've got smaller files to maintain. The final result for both pages is the same:

The output of the function

The biggest difference is you wouldn't have to redo the JavaScript for every webpage, making it easier to maintain.

Procedures and Variable Scope

There is a reason why the keyword return exists: A variable declared inside a procedure does not exist outside the function. This limited existence is known as a variable's scope.

Permit me to demonstrate by creating script with a procedure which assigns a number to a variable (but doesn't return it), calls that procedure, and tries to write the value of that variable to a specified element which will have the text Nothing written here. before the script is run:

A Script Demonstrating Limited Scope
function number(){
var num = 7;
}
number();
document.getElementById("JS").firstChild.data = num;
Something went wrong

Clearly, I did something wrong, since the text hasn't changed. Not only that, but I get the following errors from various browsers:

FireFox
Error: num is not defined
Internet Explorer
Message: 'num' is undefined
Opera
Error: . . . ReferenceError . . . Undefined variable: num
Chrome
Uncaught ReferenceError: num is not defined

When both FireFox and Internet Explorer say there's something wrong about your script, you've definitely goofed. The reason all these return an error is simple: num does not exist outside of number.

When you nest a procedure inside a procedure, the same applies.

Now, a variable declared outside of a procedure is known as a global variable. Global variables may be reference and changed within a procedure and outside it:

A Script Adjusting For Limited Scope
function number(){
num = 7;
}
var num;
number();
document.getElementById("JS").firstChild.data = num;
A function changing a variable.

Similarily, procedures that are not nested in other procedures may be called within any procedures.

This limited scope may be used to your advantage. One way that JavaScript code gets bloated is long variable names, but when scope limits the influence of variables, you can shorten those variables down to one letter each. In the function P_Full_Text, the following changes are made to the variable names:

Original NameShortened Name
loopl
p_childc
p_idi
p_textt
whatw
Script With Shortened Variable Names
function P_Full_Text(i, w){
var c = document.getElementById(i).childNodes;
if(w == "text"){
var t = "";
for(var l = 0; l < c.length; l++){
t += (c[l].nodeType == 3)?(c[l].data):(c[l].firstChild.data);
}
}
return (w == "text")?t:c.length;
}

Nesting Procedures

Procedures may contain other procedures. Like variables, these inner procedures may not be used outside the procedure they are nested in; to get their values, those values have to be passed to the procedure they are nested in. Below, I adjusted the function P_Full_Text so that the code that extracts the text from the p element is in its own procedure (in this case, it's a function).

A Procedure In A Procedure
function P_Full_Text(i, w){
var c = document.getElementById(i).childNodes;
function GetText(cl){
var t = "";
for(var l = 0; l < cl.length; l++){
t += (cl[l].nodeType == 3)?(cl[l].data):(cl[l].firstChild.data);
}
return t;
}
return (w == "text")?GetText(c):c.length;
}

What the above does is get the child node list of a p element. Depending on what the second value passed is (that is, the value w is set to), it either returns the number of child nodes or it uses the nested function Get_Text to extract the paragraph's text.

This would work even if I had GetText after the keyword return because I call the procedure before anything is returned. Its actual position within the function P_Full_Text is irrelevant—sort of. I would still get a warning from my JavaScript error console about this procedure not always returning a value. That warning goes away when I place return at the end of the procedure.

Recursive Procedures

One of the more mind-bending challenges in any programming language is a recursive procedure, which means a procedure that calls itself. This can lead to infinite loops, so you'll want a way to avoid that.

So why would I want a recursive procedure anyways?

Well, my current script will only extract text so long as the nesting level never goes past one—that is, from the p element's children. But say I wanted to extract the full text of a p element regardless of nesting? I would have to extract the text from those elements in the same way I extract the text from a p element—and I would have to allow the script to go as far as I need it to go.

In all practicality, it is impossible for this kind of recursion to go on indefinitely if it is properly programmed—if a file of any kind can be stored on a computer, there's a limit to its size and therefore a limit to the nesting in an (X)HTML document.

Below, I've updated the page created in Your First Webpage to include a third paragraph, just to illustrate things better (the table below the paragraphs is not read by the script).

The text of three paragraphs extracted.

Below is the code for those three paragraphs.

The p Elements Of The Above Page
<p id="p_1"><strong id="strong_1"><em id="em_1"><span id="span_1" style="font-size:150%;">Wel</span>come!</em></strong> This is my <em id="em_2">first</em> webpage!</p>
<p id="p_2">It's a fairly simple webpage, <strong id="strong_2"><em id="em_3">but</em></strong> it <em id="em_4">is a <strong id="strong_3">complete</strong> webpage.</em></p>
<p id="p_3">It's also fully valid, allowing me to place the <abbr id="abbr_1" title="World Wide Web Consortium">W3C</abbr> valid <abbr id="abbr_2" title="HyperText Markup Language">HTML</abbr> logo on it: <a id="a_1" href="http://validator.w3.org/"><img src="./valid-html401.png" alt="Valid HTML 4.01" id="img_1"> (Valid <abbr id="abbr_3" title="Hypertext Markup Language">HTML</abbr> Logo)</a></p>

So this page has inline elements with multiple child nodes, multiple layers of inline elements with only a single text node, and just to make everything even more challenging, there's an empty element in the third paragraph. :-)

So, let's start out with the functions P_Full_Text and GetText. However, we are going to pretty much rewrite the contents of the for loop in GetText, so let's start by getting rid of what's there.

GetText Ready To Be Rebuilt
function P_Full_Text(i, w){
var c = document.getElementById(i).childNodes;
function GetText(cl){
var t = "";
for(var l = 0; l < cl.length; l++){
// To Be Rewritten
}
return t;
}
return (w == "text")?GetText(c):c.length;
}

Now this is where logic really comes into play. The only two types of nodes that we need to get at are element nodes (type 1) and text nodes (type 3). None of the p elements contain any other type—well okay, they also contain attribute nodes, but those are really beside the point since they don't show up in a childNodes list. The childNodes property only returns types 1 and 3. Type 3 we can process right away, type 1 is the one that gets complicated, so lets set up an if/else statement to split the two types of nodes right away and deal with type 3 (text nodes).

If/Else Statement Added To GetText
function P_Full_Text(i, w){
var c = document.getElementById(i).childNodes;
function GetText(cl){
var t = "";
for(var l = 0; l < cl.length; l++){
if(cl[l].nodeType == 1){
// Coding Goes Here
} else {t += cl[l].data;}
}
return t;
}
return (w == "text")?GetText(c):c.length;
}

Now, before we do anything with an element node, I want to remind you of something: you cannot manipulate the values of a node list like you can those of an array. That means that, since cl is a node list, you cannot change the value contained in cl[l]. I tell you this with the full authority of someone who made precisely such an error, and I was almost tearing my hair out trying to fix a resulting infinite loop before I clued into my goof. Therefore, we must put the value in cl[l] into a variable we can work with (let's call it n, short for node).

Once we have done that, we'll need a way to burrow through the levels of nesting. Keep in mind the following:

Therefore, it seems to make sense that we should use a while loop that gets the first child of a node and runs so long as the length of the child node list is equal to 1.

When the loop is finished, we can take the node contained in n, and see how long its child node list is. It will be either 0 or more than 1. If it's equal to 0, then we can process the text node. If it's more than 1, then things get interesting. We'll do that as an if/else statement.

Variable Assigned Node, While Loop, And If/Else Statement Added To GetText
function P_Full_Text(i, w){
var c = document.getElementById(i).childNodes;
function GetText(cl){
var t = "";
for(var l = 0; l < cl.length; l++){
if(cl[l].nodeType == 1){
var n = cl[l];
while(n.childNodes.length == 1){n = n.firstChild}
if(n.childNodes.length > 1){
// Coding Goes Here
} else {t += n.data;}
} else {t += cl[l].data;}
}
return t;
}
return (w == "text")?GetText(c):c.length;
}

So what to do if the child node has multiple nodes? To extract its text, we need a procedure that will accept a node list and extract text from the selection of text and element nodes sent to it. As it so happens, we have one already: the GetText function itself. Placing a procedure call to GetText will cause it to be triggered with a whole new node list, the burrowing and checking starts again, and it might find other elements in one of those elements, and all around the mulberry bush we go.

In other words, GetText will be a recursive procedure.

Recursive Procedure Call Added To GetText
function P_Full_Text(i, w){
var c = document.getElementById(i).childNodes;
function GetText(cl){
var t = "";
for(var l = 0; l < cl.length; l++){
if(cl[l].nodeType == 1){
n = cl[l];
while(n.childNodes.length == 1){n = n.firstChild}
if(n.childNodes.length > 1){
t += GetText(n.childNodes); //Recursive function call here
} else {t += n.data;}
} else {t += cl[l].data;}
}
return t;
}
return (w == "text")?GetText(c):c.length;
}

Now, this will not be an infinite loop for one simple reason: in this context, infinite looping requires infinite nesting requires infinite file size. If the file size is finite, eventually this script will come to the last level of nesting and the iterations of GetText will end. Currently, there is no computer in the world that I know of that can contain such an infinite file.

With that in mind, I'd like to point out one last thing: this script is not yet finished; it needs one final touch. Earlier, I said: Text nodes do not have child nodes (thus its list of child nodes will have a length of 0). The same is true for an empty element, and we've got an img element in the third paragraph.

So, what can be said about an img element?

I don't think I need to remind you that an empty element is not a text node.

Therefore, we have to add something that will make sure that text nodes get processed, and empty elememts are ignored. We do this by turning the else statement into an else if statement, allowing us to add a condition that makes sure that what is processed is a text node.

Completed GetText Procedure
function P_Full_Text(i, w){
var c = document.getElementById(i).childNodes;
function GetText(cl){
var t = "";
for(var l = 0; l < cl.length; l++){
if(cl[l].nodeType == 1){
n = cl[l];
while(n.childNodes.length == 1){n = n.firstChild}
if(n.childNodes.length > 1){
t += GetText(n.childNodes); //Recursive function call here
} else if(n.nodeType == 3){t += n.data;}
} else {t += cl[l].data;}
}
return t;
}
return (w == "text")?GetText(c):c.length;
}

Kind of a head-spinner, isn't it? But, using logic and an understanding of precisely what you want to do, it is doäble.

Procedures As Conditions

I'd like to explain a statement I made at the beginning of this chapter: that a procedure can return a boolean value when the coding required is too complex to fit into the condition of an if statement or while loop. The example I'm going to use could technically be used in a condition, but would rapidly turn into a pain in the butt.

In Decisions, I demonstrated an if condition that depended on only one condition proving true and the other proving false:

Only One Or The Other Can Be True
if(
(foo == "five" && bar != 5) ||
(foo != "five" && bar == 5)
){
// Code goes here
}

Here is a similar condition, allowing only one of three conditions to be true:

Only One Of Three Can Be True
if(
(foo == "five" && bar != 5 && dom_node.nodeType != 3) ||
(foo != "five" && bar == 5 && dom_node.nodeType != 3) ||
(foo != "five" && bar != 5 && dom_node.nodeType == 3)
){
// Code goes here
}

Imagine the fun if this required ten conditions...

In Decisions, I demonstrated that assigning a condition to a variable would result in that variable holding a boolean value, depending on the result of that condition. You can assign boolean values to an array in similar fashion:

An Array Of Conditions
var conditions = new Array(
(foo == "five"),
(bar == 5),
(dom_node.nodeType == 3)
)

This will actually result in an array of boolean values, which is exactly what we want for the procedure we are creating. Let's call this procedure XOR after a keyword does exactly this in other programming languages (I think it means Exclusive OR). The desired effect can be achieved in two ways:

  1. A loop goes through the array elements until it finds one that is true (or until it runs out of elements). A second loop checks the rest of the values to see if any of those are true.
  2. A loop goes through all the array elements and counts how many are true.
The XOR Function Using The First Method
function XOR(conds){
var bool = false;
var count = 0;
while(!bool && count < conds.length){
bool = conds[count];
count++;
}
while(bool && count < conds.length){
bool = !conds[count];
count++;
}
return bool;
}

Note the subtle distinction between the first and second while loops: the first runs so long as bool is false, ending when bool is assigned a value of true. The second runs so long as bool is true, ending when bool is assigned a value of false)—that is, because of the use of !, literally not true.

The second method uses one loop, and goes on so long as the number of true elements is less than or equal to 1.

The XOR Function Using The Second Method
function XOR(conds){
var trucount = 0;
var count = 0;
while(!trucount <= 1 && count < conds.length){
trucount += (conds[count])1:0;
count++;
}
return (trucount == 1);
}

The second method requires a bit less coding and allows for some extra flexibility, which I'll get to in just a bit.

In either case, the function call is identical:

Using XOR In A Condition
if(XOR([
(foo == "five"),
(bar == 5),
(dom_node.nodeType == 3)
])){
// Coding Goes Here
}

Here's the flexibility I mentioned earlier: the second method allows you to pass a second variable to determine how many conditions you want to prove true.

The XOR Function With Added Flexibility
function XOR(conds, trulimit){
if(trulimit === undefined){trulimit = 1;}
var trucount = 0;
var count = 0;
while(!trucount <= trulimit && count < conds.length){
trucount += (conds[count])1:0;
count++;
}
return (trucount == trulimit);
}

The if statement at the beginning of XOR means that if trulimit isn't set in the function call (that is, if the second value is ommitted), then it is set to its default value of 1.

Using XOR In A Condition
Only One Can Prove true
if(XOR([
(foo == "five"),
(bar == 5),
(dom_node.nodeType == 3)
])){
// Coding Goes Here
}
Exactly Two Must Prove true
if(XOR([
(foo == "five"),
(bar == 5),
(dom_node.nodeType == 3)
], 2)){
// Coding Goes Here
}

In any case, using a function in a condition allows for such code to become much simpler—and in some cases, even possible. For an example in the possible category, I used a function called InArray in the script for an old page that allowed you to experiment with colours to check if a string is in an array of colour names.

The InArray Function
The Procedure Itself
function InArray($arr, $arr_val){
var $bool = false;
var $count = 0;
do{
$bool = ($arr[$count] == $arr_val)?true:false;
$count++;
}while($count < $arr.length && !$bool);
return $bool;
}
Calling The Procedure
InArray($valid_cols, $nm.toLowerCase());

In this particular script, $nm stands for a colour name, and $valid_cols refers to an array of seventeen colour names that the W3C recognizes as valid CSS names. For some reason, I used dollar signs ("$") in front of my variable names in this script. I think it was because I do so much work with PHP, which requires them. Many coders use dollar signs in front of variable names to make it clear that they ARE variables, but they aren't required in JavaScript.