Chatper 6

Using Interactive Forms


CONTENTS

In this chapter you'll explore one of the most powerful uses for JavaScript: working with HTML forms. You can use JavaScript to make a form more interactive, to validate data the user enters, and to enter data based on other data.

Let's begin with a basic order form for an imaginary company and use JavaScript to add features to the form. Along the way you'll learn about the various form elements, how they relate to JavaScript, and just what you can do with each one.

Building a JavaScript-Compatible HTML Form

HTML started out as a read-only language; you could present information to readers and allow them to follow links to sites with different information, but there was no way to accept input from the user.

The advent of HTML forms changed things considerably. Forms added new HTML tags that could take input from the user. This enables you easily to create a fill-out form to register users or take orders, or for any other purpose.

Before you build a sample form, let's start with a quick overview of the elements you can use in a form. Each of these will relate to a JavaScript object, which is discussed later in this chapter.

Understanding Form Actions and Methods

By itself, an HTML form can only hold information; it can't send it to the server. This part of the job is accomplished by Common Gateway Interface (CGI), a standard for server-side applications. When you press the SUBMIT button on a form, the data in the fields is sent to the CGI program, which can act on the data-mail you a response, send a result back to the user, or add an item to a database.

When you submit the form data, it is sent to the server using one of two methods:

You'll learn about the technical aspects of these methods and which you should use in Chapter 17, "Combining JavaScript, CGI, and SSI." Basically, it will depend on the data and on what the CGI script you are using supports. For the moment, you can use either one.

Because JavaScript can't communicate with the server, it doesn't eliminate the need for CGI; instead, it enables you to control the data as it is entered and check it for accuracy before the CGI program receives it. This saves you work creating the CGI program. In fact, you can easily use the same CGI program for many different forms. One such script is presented in Chapter 17.

Tip
It is possible to have a JavaScript-only form. You used simple forms in Chapter 5 "Accessing Window Elements as Objects," to interact with the user. You'll find many more examples throughout this book.

After the CGI script receives the data, it sends a response back to the user. This can send the user to a different Web page or can generate a page "on the fly" and display it.

Overview of Form Elements

An HTML form begins with the <FORM> tag. This tag indicates that a form is beginning, and it enables form elements to be used. The <FORM> tag includes three parameters:

For example, here is a <FORM> tag for a form named Order. This form uses the GET method and sends its data to a CGI script called order.cgi in the same directory as the Web page itself:

<FORM NAME="Order" METHOD="GET" ACTION="order.cgi">

For a form that will be processed entirely by JavaScript (such as a calculator or interactive game), the METHOD and ACTION attributes are not needed. You can use a simple <FORM> tag that names the form:

<FORM NAME="calcform">

The <FORM> tag is followed by one or more form elements. These are the data fields in the form, such as text fields and checkboxes. You will look at each type of element in the following sections. After all the elements comes the form ending tag, </FORM>. After this tag, you can't use form elements without starting another form.

Note
You can also include any normal HTML elements within the <FORM> tags. This is useful for labeling each of the elements.

Text Fields and Text Areas

Many of the form elements use the <INPUT> tag. This tag is followed by a TYPE parameter to determine which type of input is expected. The first such field is the TEXT field. This is the simplest of the form elements; it enables the user to enter text in a one-line area. The following is an example of a simple TEXT field:

<INPUT TYPE="TEXT" NAME="text1" VALUE="hello" SIZE="30">

This defines a text field called text1. The field is given a default value of "hello" and allows up to 30 characters to be entered.

An alternate form of text field is the PASSWORD field. This is a specialized text field that displays the text as asterisks on the screen. This type of input is often used for passwords so that observers don't see which password is being entered. The password field is defined like TEXT:

<INPUT TYPE="PASSWORD" NAME="pass1" SIZE=30>

A third option is the text area, which allows multiple lines of text to be entered. Rather than using the <INPUT> tag, text areas are defined with a special tag, <TEXTAREA>. Here is a typical <TEXTAREA> definition:

<TEXTAREA NAME="text1" ROWS="2" COLS="70">
This is the content of the TEXTAREA tag.
</TEXTAREA>

The text between the opening and closing <TEXTAREA> tags is used as the initial value for the text area. This type of tag is ideal when you need a larger amount of information, such as a complete address or a paragraph of text. You can include line breaks within the default value.

Figure 6.1 shows examples of TEXT, PASSWORD, and TEXTAREA tags on an HTML page, as displayed by Netscape.

Figure 6.1 : An example of the TEXT, PASSWORD, and TEXTAREA form elements.

Checkboxes, Radio Buttons, and Selection Lists

For some parts of your form, you may want to ask simple questions: yes/no questions, multiple-choice questions, and so on. Three form elements make it easy to do this.

The first element is the checkbox; this simply displays a box that can be checked or unchecked. The chECKBOX type to the INPUT tag is used to create a checkbox, as follows:

<INPUT TYPE="chECKBOX" NAME="check1" VALUE="Yes" chECKED>

Again, this gives a name to the form element. The VALUE attribute assigns a meaning to the checkbox; this is a value that is returned if the box is checked. The default value is "on." Finally, the chECKED attribute can be included to make the box checked by default.

Another element for decisions is the radio button, using the <INPUT> tag's RADIO type. These buttons are similar to checkboxes, but they exist in groups, and only one button can be checked in each group. These are used for a multiple-choice or "one of many" input. Here's an example of a group of radio buttons:

<INPUT TYPE="RADIO" NAME="radio1" VALUE="Option1" chECKED> Option 1
<INPUT TYPE="RADIO" NAME="radio1" VALUE="Option2"> Option 2
<INPUT TYPE="RADIO" NAME="radio1" VALUE="Option3"> Option 3

Once again, the NAME attribute is used; in this case, it names the entire group of radio buttons. All the buttons with the same name are considered to be in a group. The VALUE attribute gives each button a name; this is essential so that you can tell which one is pressed.

A final form element is also useful for multiple-choice selections: the <SELECT> HTML tag is used to define a selection list, or a multiple-choice list of text items. This is an example of a selection list:

<SELECT NAME="select1" SIZE=40>
<OPTION VALUE="choice1" SELECTED>This is the first choice.
<OPTION VALUE="choice2">This is the second choice.
<OPTION VALUE="choice3">This is the third choice.
</SELECT>

Each of the OPTION tags defines one of the possible choices. The VALUE attribute is the name that is returned to the program, and the text outside the OPTION tag is displayed as the text of the option.

An optional attribute to the SELECT tag, MULTIPLE, can be specified to allow multiple items to be selected. Browsers usually display a single-selection SELECT as a drop-down list and a multiple-selection list as a scrollable list.

Figure 6.2 is an example of an HTML page in Netscape that shows examples of checkboxes, radio buttons, and single- and multiple-selection lists.

Figure 6.2 : An example of the display of checkboxes, radio buttons, and selection lists.

Hidden Fields

HTML form elements include a special type of field: the hidden field. This includes a name and a value, similar to a text field; however, you can't edit it, and it is not displayed on the Web page. Hidden fields are useful for passing information to the CGI script or between multiple scripts.

A hidden field uses the <INPUT> tag and simply has a name and a value. For example, this command defines a hidden field called score with a value of 40200:

<INPUT TYPE="HIDDEN" NAME="score" VALUE="40200">

SUBMIT and RESET Buttons

The final type of form element is a button. Buttons use the <INPUT> tag and can use one of three different types:

All three types of buttons include a NAME to identify the button, and a VALUE that indicates the text to display on the button's face. A few buttons were used in the examples in Chapter 5. As an example, the following defines a SUBMIT button with the name sub1 and the value "Click Here":

<INPUT TYPE="SUBMIT" NAME="sub1" VALUE="Click Here">

Creating an HTML Form

Using the form elements discussed, let's create an order form for a fictional software company called, coincidentally enough, Fictional Software Company (FSC). This company sells four products: a word processor, a spreadsheet, a database, and an instruction booklet. You will use the following elements:

Listing 6.1 shows the complete HTML form, and Figure 6.3 shows Netscape's display of the form.

Figure 6.3 : The first draft of the FSC order form.


Listing 6.1. The HTML document for the order form.
<HTML>
<HEAD><TITLE>Order Form</TITLE></HEAD>
<BODY>
<H1>Order Form</H1>
<FORM NAME="order">
<B>Name:</B> <INPUT TYPE="text" NAME="name1" SIZE=20>
<B>Phone: </B><INPUT TYPE="text" NAME="phone" SIZE=15>
<B>E-mail address:</B><INPUT TYPE="text" NAME="email" SIZE=20><BR>
<B>Billing and Shipping Addresses:</B><BR>
<TEXTAREA NAME="billto" COLS=40 ROWS=4>
Enter your billing address here.
</TEXTAREA>
<TEXTAREA NAME="shipto" COLS=40 ROWS=4>
Enter your shipping address here.
</TEXTAREA>
<B>Products to Order:</B><BR>
Qty: <INPUT TYPE="TEXT" NAME="qty1" VALUE="0" SIZE=4>
Cost: <INPUT TYPE="TEXT" NAME="cost1" SIZE=6>
($40.00 ea) Fictional Spreadsheet 7.0 <BR>
Qty: <INPUT TYPE="TEXT" NAME="qty2" VALUE="0" SIZE=4>
Cost: <INPUT TYPE="TEXT" NAME="cost2" SIZE=6>
($69.95 ea) Fictional Word Processor 6.0<BR>
Qty: <INPUT TYPE="TEXT" NAME="qty3" VALUE="0" SIZE=4>
Cost: <INPUT TYPE="TEXT" NAME="cost3" SIZE=6>
($99.95 ea) Fictional Database 7.0 <BR>
Qty: <INPUT TYPE="TEXT" NAME="qty4" VALUE="0" SIZE=4>
Cost: <INPUT TYPE="TEXT" NAME="cost4" SIZE=6>
($4.95 ea) Instruction Booklet for the above <HR>
<B>Total Cost:</B>
<INPUT TYPE="TEXT" NAME="totalcost" SIZE=8><HR>
<B>Method of Payment</B>:
<SELECT NAME="payby">
<OPTION VALUE="check" SELECTED>Check or Money Order
<OPTION VALUE="cash">Cash or Cashier's Check
<OPTION VALUE="credit">Credit Card (specify number)
</SELECT><BR>
<B>Credit Card or Check Number:</B>:
<INPUT TYPE="TEXT" NAME="creditno" SIZE="20"><BR>
<INPUT TYPE="SUBMIT" NAME="submit" VALUE="Send Your Order">
<INPUT TYPE="RESET" VALUE="Start Over">
</FORM>
</BODY>
</HTML>

Forms like this one are used all over the Web. As you can see, this form isn't very user-friendly; the user has to calculate the cost for each item and the total cost. You will use JavaScript to add automatic calculation and other interactive features later in this chapter.

Using form Objects in JavaScript

Before you continue working with the order form, let's look at the way JavaScript handles form data. Each form in your HTML page is represented in JavaScript by a form object. The form object has the same name as the NAME attribute in the <FORM> tag you used to define it.

Alternately, you can use the forms array to refer to forms. This array includes an item for each form element, indexed starting with zero. For example, if the first form in a document has the name form1, you can refer to it in one of two ways:

document.form1
document.forms[0]

form Object Properties

The most important property of the form object is the elements array, which contains an object for each of the form elements. You can refer to an element by its own name or by its index in the array. For example, these expressions both refer to the first element in the order form, the name1 text field:

document.order.elements[0]
document.order.name1

Note
Both forms and elements can be referred to with their own names, or as indices in the forms and elements arrays. For clarity, this chapter will use individual form and element names rather than array references.

If you do refer to forms and elements as arrays, you can use the length property to determine the number of objects in the array: document.forms.length is the number of forms in a document, and document.form1.elements.length is the number of elements in the form1 form.

Along with the elements, each form object also has a list of properties, most of which are defined by the corresponding <FORM> tag. You can also set these from within JavaScript. They include the following:

form Object Methods and Events

The form object has two event handlers, onSubmit and onReset. You can specify a group of JavaScript statements or a function call for these events within the <FORM> tag that defines the form.

If you specify a statement or function for the onSubmit event, the statement is called before the data is submitted to the CGI script. You can prevent the submission from happening by returning a value of false from the onSubmit event handler. If the statement returns true, the data will be submitted. In the same fashion, you can prevent a RESET button from working with an onReset event handler.

The form object has two methods, submit() and reset(). You can use these methods to submit the data or reset the form yourself, without requiring the user to press a button. The submit() method should be used only in special cases; it's generally bad manners to send data to the server without the user's permission.

Note
You can use a mailto URL in the ACTION attribute of a form and have the data mailed to you instead of submitted to a CGI script. Using the submit() method with such an action enabled scripts to obtain information about your computer or to send mail from your address without your knowledge. Because of this potential security and privacy concern, Netscape disabled the submit method for mailto URLs.

The JavaScript form Object Hierarchy

The elements of a form each have their own object classes, and each of these has certain properties, methods, and events. In the next sections, you will look at each type of JavaScript object used for form elements in detail.

One property applies to all form elements: the type property is a string that describes the type of element. For most of the elements, this is the value you specified in the TYPE attribute of the <INPUT> tag. The exceptions are the following:

Another property that applies to all form elements is the form property, which indicates an element's parent form object.

Text Fields and Password Fields

Text fields are the simplest field to work with in JavaScript. Password fields are nearly identical; the main difference is that the text is not displayed. The text and password objects have the following properties:

Most of the time when you work with text fields, you will use the value attribute to read the value the user has entered, or to change the value. For example, this statement changes the value of a text field called username in the order form to "John Q. User":

document.order.username.value = "John Q. User"

Note
For security reasons, you cannot normally access the value property of a password object in JavaScript. You can access it if you enable data tainting, as explained in Chapter 10, "Working with Multiple Pages and Data."

Text Field Methods

The text and password objects also have a few methods you can use:

Tip
These aren't the only methods you can use with text fields; don't forget that because the value property is a String object, you can use any of the String methods on the value. The String object is explained in Chapter 4 "Using Built-In Objects and Custom Objects."

Text Field Events

The text and password objects support the following event handlers:

If used, these event handlers should be included in the <INPUT> tag declaration. For example, the following is a text field including an onChange event that displays an alert:

<INPUT TYPE="TEXT" NAME="text1" onChange="window.alert('Changed.');">

Text Areas

Text areas are defined with their own tag, <TEXTAREA>, and are represented by the textarea object. This object includes the same properties, methods, and event handlers as the text and password objects. For example, this <TEXTAREA> tag includes an onFocus event handler to change the status line:

<TEXTAREA NAME="text2" onFocus = "window.status = 'got focus.';">Default Value</TEXTAREA>

There is one difference about a text area's value: it can include more than one line, with end-of-line characters between. This can be complicated by the fact that the end-of-line character is different on the three platforms (\r\n in Windows; \n in UNIX and Macintosh). Be sure to check for both types when needed.

If you are placing your own values into a text field, the latest version of JavaScript automatically converts any end-of-line characters to match the user's platform, so you can use either type.

Note
This is an example of a platform-specific issue within JavaScript; although JavaScript is intended as a platform-independent language, there are still a few stubborn features. See Chapter 14, "Debugging JavaScript Programs," for additional information.

Checkboxes

A checkbox is simple: it has only two states. Nevertheless, the checkbox object has four different properties:

To manipulate the checkbox or use its value, you use the checked attribute. For example, this statement turns on a checkbox called same in the order form:

document.order.same.checked = true;

The checkbox has a single method, click(). This method simulates a click on the box. It also has a single event, onClick, which occurs whenever the checkbox is clicked. This happens whether the box was turned on or off, so you'll need to check the checked property.

Caution
The click() method does not work properly in some versions of Netscape. You should avoid it when possible.

Radio Buttons

Radio buttons are similar to checkboxes, but an entire group of them shares a single name and a single object. You can refer to the following properties of the radio object:

To access individual buttons, you treat the radio object as an array. The buttons are indexed, starting with 0. Each individual button has the following properties:

For example, you can check the first radio button in the radio1 group on the form1 form with this statement:

document.form1.radio1[0].checked = true;

However, if you do this, be sure you set the other values to false as needed. This is not done automatically. You can use the click method to do both of these in one step.

Like a checkbox, radio buttons have a click() method and an onClick event handler. Each radio button can have a separate statement for this event.

Note
A bug in Netscape Navigator 2.0 causes radio buttons to be indexed backward; index 0 will actually be the last button on the page. This is fixed in version 3.0 and later, but watch out for strange behavior when users use an old version.

Selection Lists

A selection list is similar to radio buttons, because the entire group of options shares a name. In this case, the data for each element is stored in the options array, which indexes the different options starting with 0.

The object for selection lists is the select object. The object itself has the following properties:

The options array has a single property of its own, length, which indicates the number of selections. In addition, each item in the options array has the following properties:

The select object has two methods, blur() and focus(). These perform the same purpose as the corresponding methods for text objects. The event handlers are onBlur, onFocus, and onChange, also similar to other objects.

You can change selection lists dynamically-for example, choosing a product in one list could control which options are available in another list. You can also add and delete options from the list. You will look at these capabilities in Chapter 11, "Real-Life Examples II."

Note
The onChange event doesn't work for select lists in some versions of Netscape . It does work properly in version 3.0 and later.

Hidden Fields

Hidden fields are stored in hidden objects. This object has two properties, name and value. These function similarly to a text field. There are no methods or event handlers for the hidden object-it can't be changed, so the user can't really mess with a hidden field.

Buttons

Buttons can be defined as SUBMIT buttons, RESET buttons, or generic BUTTON buttons. All of these types have the same properties, methods, and events.

Buttons support the name property, used to identify the button, and the value property, which defines the button's text. You cannot change either of these values. Buttons support a single method, click(), and an event handler, onClick.

The onClick action is performed with any button. In the case of a generic button, nothing else happens. In a SUBMIT or RESET button, you can prevent the submission or resetting by returning a false value.

Note
As with radio buttons and checkboxes, the click method may not work with some older versions of Netscape. Check your version before attempting to use it, and expect trouble from users of older versions.

File Upload Fields

A relatively new feature of Netscape enables you to define a file upload field on a form. This enables the user to upload a file to the server. You can define a file upload field with an <INPUT> tag:

<INPUT TYPE="file" NAME="fname">

Because this field is mainly for interacting with the server, JavaScript has little control over it. A FileUpload object represents the field, and you can access two properties:

Automating the Form with JavaScript

You now have quite a bit of information about the many form-related objects and how they can be used in JavaScript. Let's bring this information into the real world by demonstrating how you can add friendly, interactive features to the FSC order form created earlier in this chapter.

Adding Automatic Totals

At present, the order form isn't any more convenient than a paper one-the user still has to calculate the total for each item and the grand total manually. Let's use JavaScript to make those functions automatic. You will do this with the onChange event handler, so each time a quantity is changed, the associated totals will be updated.

To start with, you will make each item's cost field update when its quantity field is changed. To do this, you'll use the onChange event handler for each of the quantity text fields. For example, here's what the first one will look like:

Qty: <INPUT TYPE="TEXT" NAME="qty1" VALUE="0" SIZE=4
onChange = "UpdateCost(1, 40.00);">

This calls a function called UpdateCost(), which takes two parameters: the item number to update (1 through 4) and the associated unit cost. You then need to define the UpdateCost() function in the HTML header. Here's the function:

// function to update cost when quantity is changed
function UpdateCost(number, unitcost) {
   costname = "cost" + number;
   qtyname = "qty" + number;
   var q = document.order[qtyname].value;
   document.order[costname].value = q * unitcost;
   Total();
}

This function stores the item number and unit cost in the number and unitcost variables. It then constructs names for the cost and quantity elements; because they have been named consistently (such as qty1 through qty4), this is simple.

A temporary variable, q, is used to hold the quantity field's current value. Notice that you have to use brackets around qtyname, because you want to use the value of this variable, not its actual name. The quantity in q is then multiplied by the unit cost and stored in the appropriate cost text field.

Finally, you include a function call for the Total() function, which will calculate the total cost each time a field is changed. Here is the definition for this function:

// function to calculate the total cost field
function Total() {
   var tot = 0;
   tot += (40.00 * document.order.qty1.value);
   tot += (69.95 * document.order.qty2.value);
   tot += (99.95 * document.order.qty3.value);
   tot += (4.95 * document.order.qty4.value);
   document.order.totalcost.value = tot;

}

This function simply uses a temporary variable called tot to add up each of the costs, which it gets by multiplying the appropriate prices by the quantities. It then stores the value of tot in the total cost text field.

Listing 6.2 shows the revised HTML order form including the new functions. You now have an order form that updates automatically-each time you enter or change a quantity, the cost for that item and the total cost are updated. Figure 6.4 shows the order form after several quantities have been entered.

Figure 6.4 : The order form with automatically updated numeric totals.


Listing 6.2. The revised HTML order form, including automatic update functions.
<HTML>
<HEAD><TITLE>Order Form</TITLE>
<SCRIPT>
// function to calculate the total cost field
function Total() {
   var tot = 0;
   tot += (40.00 * document.order.qty1.value);
   tot += (69.95 * document.order.qty2.value);
   tot += (99.95 * document.order.qty3.value);
   tot += (4.95 * document.order.qty4.value);
   document.order.totalcost.value = tot;
}

// function to update cost when quantity is changed
function UpdateCost(number, unitcost) {
   costname = "cost" + number;
   qtyname = "qty" + number;
   var q = document.order[qtyname].value;
   document.order[costname].value = q * unitcost;
   Total();
}
</SCRIPT>
</HEAD>
<BODY>
<H1>Order Form</H1>
<FORM NAME="order">
<B>Name:</B> <INPUT TYPE="text" NAME="name1" SIZE=20>
<B>Phone: </B><INPUT TYPE="text" NAME="phone" SIZE=15>
<B>E-mail address:</B><INPUT TYPE="text" NAME="email" SIZE=20><BR>
<B>Billing and Shipping Addresses:</B><BR>
<TEXTAREA NAME="billto" COLS=40 ROWS=4>
Enter your billing address here.
</TEXTAREA>
<TEXTAREA NAME="shipto" COLS=40 ROWS=4>
Enter your shipping address here.
</TEXTAREA>
<B>Products to Order:</B><BR>
Qty: <INPUT TYPE="TEXT" NAME="qty1" VALUE="0" SIZE=4
onChange = "UpdateCost(1, 40.00);">
Cost: <INPUT TYPE="TEXT" NAME="cost1" SIZE=6>
($40.00 ea) Fictional Spreadsheet 7.0 <BR>
Qty: <INPUT TYPE="TEXT" NAME="qty2" VALUE="0" SIZE=4
onChange = "UpdateCost(2, 69.95);">
Cost: <INPUT TYPE="TEXT" NAME="cost2" SIZE=6>
($69.95 ea) Fictional Word Processor 6.0<BR>
Qty: <INPUT TYPE="TEXT" NAME="qty3" VALUE="0" SIZE=4
onChange = "UpdateCost(3, 99.95);">
Cost: <INPUT TYPE="TEXT" NAME="cost3" SIZE=6>
($99.95 ea) Fictional Database 7.0 <BR>
Qty: <INPUT TYPE="TEXT" NAME="qty4" VALUE="0" SIZE=4
onChange = "UpdateCost(4, 4.95);">
Cost: <INPUT TYPE="TEXT" NAME="cost4" SIZE=6>
($4.95 ea) Instruction Booklet for the above <HR>
<B>Total Cost:</B>
<INPUT TYPE="TEXT" NAME="totalcost" SIZE=8><HR>
<B>Method of Payment</B>:
<SELECT NAME="payby">
<OPTION VALUE="check" SELECTED>Check or Money Order
<OPTION VALUE="cash">Cash or Cashier's Check
<OPTION VALUE="credit">Credit Card (specify number)
</SELECT><BR>
<B>Credit Card or Check Number:</B>:
<INPUT TYPE="TEXT" NAME="creditno" SIZE="20"><BR>
<INPUT TYPE="SUBMIT" NAME="submit" VALUE="Send Your Order">
<INPUT TYPE="RESET" VALUE="Start Over">
</FORM>
</BODY>
</HTML>

Automating the Shipping Address

Because the order form includes spaces for separate billing and shipping addresses, it's a good idea to give the user an option to use the same address for both. Using JavaScript, you can add such an option easily-and it makes for a neat bit of automation.

To accomplish this, you'll add a checkbox above the addresses and label it:

<INPUT TYPE="chECKBOX" NAME="same" onClick="CopyAddress();">
Ship to Billing Address

You'll use the onClick event handler for the checkbox to handle the copying. Here is the CopyAddress function for copying the address:

// function to copy billing address to shipping address
function CopyAddress() {
   if (document.order.same.checked) {
      document.order.shipto.value = document.order.billto.value;
   }
}

This function checks the checked property of the checkbox, and if it's currently checked, it copies the billing address to the shipping address.

This works when the checkbox is checked, but what about when the user changes the billing address after checking the box? You will use the onChange event handler to do the copy again whenever the billing address changes:

<TEXTAREA NAME="billto" COLS=40 ROWS=4 onChange="CopyAddress();">
Enter your billing address here.
</TEXTAREA>

There's one possibility left: after checking the box, the user might mistakenly change the shipping address. Because you want the addresses to be the same, you use the same event handler on the shipping address. Therefore, when the user changes
the shipping address with the box checked, it will immediately be changed back to the billing address.

Listing 6.3 is the latest version of the HTML form, including the automatic address-copying function you just added. Figure 6.5 shows how the new form looks in Netscape.

Figure 6.5 : The order form with address-copying feature.


Listing 6.3. The revised HTML order form with the address-copying features.
<HTML>
<HEAD><TITLE>Order Form</TITLE>
<SCRIPT>
// function to calculate the total cost field
function Total() {
   var tot = 0;
   tot += (40.00 * document.order.qty1.value);
   tot += (69.95 * document.order.qty2.value);
   tot += (99.95 * document.order.qty3.value);
   tot += (4.95 * document.order.qty4.value);
   document.order.totalcost.value = tot;
}

// function to update cost when quantity is changed
function UpdateCost(number, unitcost) {
   costname = "cost" + number;
   qtyname = "qty" + number;
   var q = document.order[qtyname].value;
   document.order[costname].value = q * unitcost;
   Total();
}

// function to copy billing address to shipping address
function CopyAddress() {
   if (document.order.same.checked) {
      document.order.shipto.value = document.order.billto.value;
   }
}

</SCRIPT>
</HEAD>
<BODY>
<H1>Order Form</H1>
<FORM NAME="order">
<B>Name:</B> <INPUT TYPE="text" NAME="name1" SIZE=20>
<B>Phone: </B><INPUT TYPE="text" NAME="phone" SIZE=15>
<B>E-mail address:</B><INPUT TYPE="text" NAME="email" SIZE=20><BR>
<B>Billing and Shipping Addresses:</B>
<INPUT TYPE="chECKBOX" NAME="same" onClick="CopyAddress();">
Ship to Billing Address
<BR>
<TEXTAREA NAME="billto" COLS=40 ROWS=4 onChange="CopyAddress();">
Enter your billing address here.
</TEXTAREA>
<TEXTAREA NAME="shipto" COLS=40 ROWS=4 onChange="CopyAddress();">
Enter your shipping address here.
</TEXTAREA>
<B>Products to Order:</B><BR>
Qty: <INPUT TYPE="TEXT" NAME="qty1" VALUE="0" SIZE=4
onChange = "UpdateCost(1, 40.00);">
Cost: <INPUT TYPE="TEXT" NAME="cost1" SIZE=6>
($40.00 ea) Fictional Spreadsheet 7.0 <BR>
Qty: <INPUT TYPE="TEXT" NAME="qty2" VALUE="0" SIZE=4
onChange = "UpdateCost(2, 69.95);">
Cost: <INPUT TYPE="TEXT" NAME="cost2" SIZE=6>
($69.95 ea) Fictional Word Processor 6.0<BR>
Qty: <INPUT TYPE="TEXT" NAME="qty3" VALUE="0" SIZE=4
onChange = "UpdateCost(3, 99.95);">
Cost: <INPUT TYPE="TEXT" NAME="cost3" SIZE=6>
($99.95 ea) Fictional Database 7.0 <BR>
Qty: <INPUT TYPE="TEXT" NAME="qty4" VALUE="0" SIZE=4
onChange = "UpdateCost(4, 4.95);">
Cost: <INPUT TYPE="TEXT" NAME="cost4" SIZE=6>
($4.95 ea) Instruction Booklet for the above <HR>
<B>Total Cost:</B>
<INPUT TYPE="TEXT" NAME="totalcost" SIZE=8><HR>
<B>Method of Payment</B>:
<SELECT NAME="payby">
<OPTION VALUE="check" SELECTED>Check or Money Order
<OPTION VALUE="cash">Cash or Cashier's Check
<OPTION VALUE="credit">Credit Card (specify number)
</SELECT><BR>
<B>Credit Card or Check Number:</B>:
<INPUT TYPE="TEXT" NAME="creditno" SIZE="20"><BR>
<INPUT TYPE="SUBMIT" NAME="submit" VALUE="Send Your Order">
<INPUT TYPE="RESET" VALUE="Start Over">
</FORM>
</BODY>
</HTML>

Validating the Form Data with Event Handlers

The final feature you can add to a form with JavaScript is perhaps the most important: validation. This means checking each field to ensure that it contains a proper value and advising the user if it is incorrect.

Obviously, validating can only do so much. For example, you can't tell whether a phone number is valid or whether a name is an alias. You can tell whether it exists, though, and whether it's in the right format.

Validation saves you some trouble when you receive a response. More importantly, it saves you the trouble of performing the validation in the CGI script. This enables you to use a generic CGI script, and enables the user to receive immediate feedback without waiting on the server.

Where to Validate?

You could use the onChange event handler on each of the fields to validate it. For example, as soon as users enter their names and move to the next field, you could alert them if the names are invalid.

This method is worth considering in some cases, but for most forms it's best to validate all the fields at once, using the form's onSubmit event handler. This enables users to correct any fields necessary, then press submit when they're really ready.

For the FSC order form, you will use a single onSubmit event handler to perform the validation for all the fields.

Tip
If you choose to validate fields as they change, you might find it useful to use the this keyword. Within an event handler, this represents the object that triggered the event. This technique enables you to use a single validation routine for several fields.

Which Fields to Validate?

When choosing how to handle validation for a form, the first step is to decide which fields you need to validate and what to check for. For some items, such as a name, the most you can do is see whether anything is entered.

For other items you can be more specific; for example, if your form asked for the user's age, you can check for numbers over 100 or so. (I would suggest a lower limit, but the Internet audience is getting younger every day.)

For the FSC order form, let's use the following validation criteria:

Obviously, you could make some of these more specific, but they'll do to illustrate the concept. You'll need to choose the appropriate values for each form with which you work.

Creating Functions for Validation

Based on the decisions made previously, you now need to create a function to handle each of the fields. In some cases, you can use the same function for multiple fields. Let's start with the simplest function: checking for the proper length. This function will work with the name, billing address, and phone number fields.

function ValidLength(item, len) {
   return (item.length >= len);

}

This function expects the name of an item and the required length. It simply checks whether the item is greater than or equal to that length, and it returns true or false appropriately.

Next, let's create a function for the e-mail address. This will simply call the ValidLength function to check the length, then use the indexOf string method to check for the @ sign:

//function to validate an email address
function ValidEmail(item) {
   if (!ValidLength(item, 5)) return false;
   if (item.indexOf ('@', 0) == -1) return false;
   return true;
}

Finally, let's create a main validation routine. This routine validates all the fields one at a time, then returns true or false:

function Validate() {
   errfound = false;
   if (!ValidLength(document.order.name1.value,6))
      error(document.order.name1,"Invalid Name");
   if (!ValidLength(document.order.phone.value,10))
      error(document.order.phone,"Invalid Phone");
   if (!ValidLength(document.order.billto.value,30))
      error(document.order.billto,"Invalid Billing Address");
   if (!ValidLength(document.order.shipto.value,30))
      error(document.order.shipto,"Invalid Shipping Address");
   if (!ValidEmail(document.order.email.value))
      error(document.order.email, "Invalid Email Address");
   if (document.order.totalcost.value == "")
      error(document.order.qty1, "Please Order at least one item.");
   if (document.order.payby.selectedIndex != 1) {
      if (!ValidLength(document.order.creditno.value,2))
         error(document.order.creditno,"Invalid Credit/Check number");
   }
   return !errfound; /* true if there are no errors */
}

As you can see, this function includes tests for the length-related functions and the e-mail address; in addition, I have added checks for the credit card number and the total cost. If any of these checks fails, an error routine is called:

function error(elem, text) {
// abort if we already found an error
   if (errfound) return;
   window.alert(text);
   elem.select();
   elem.focus();
   errfound = true;
}

This function displays a dialog explaining the error, then sets the focus to the element with the error; this positions the cursor in that field so the user can easily change it. In addition, the select method is used to select the item's text.

Note
Because the user doesn't normally enter data in the total cost field, the validate routine points the cursor to the first quantity field, so the user can add an item to the order.

One more thing: the error function sets a variable, errfound, to indicate that an error has happened. It returns immediately if an error was already found; this prevents multiple dialogs from showing if more than one field is invalid.

Adding an Event Handler for Validation

Finally, you can link the validation routine to the form itself. You will add an onSubmit event handler to the FORM declaration:

<FORM NAME="order" onSubmit="return Validate();">

This calls the Validate function and uses its return value as the return value of the event. This means that if Validate returns false-in other words, if something is invalid-the data will not be submitted.

This basically completes the order form. Listing 6.4 is the complete HTML listing, including all the validation scripting. The complete form is shown in Figure 6.6, including a dialog indicating that the e-mail address is invalid.

Figure 6.6 : The complete HTML order form, including validation for all fields.


Listing 6.4. The revised HTML order form with validation.
<HTML>
<HEAD><TITLE>Order Form</TITLE>
<SCRIPT>
// function to calculate the total cost field
function Total() {
   var tot = 0;
   tot += (40.00 * document.order.qty1.value);
   tot += (69.95 * document.order.qty2.value);
   tot += (99.95 * document.order.qty3.value);
   tot += (4.95 * document.order.qty4.value);
   document.order.totalcost.value = tot;
}
// function to update cost when quantity is changed
function UpdateCost(number, unitcost) {
   costname = "cost" + number;
   qtyname = "qty" + number;
   var q = document.order[qtyname].value;
   document.order[costname].value = q * unitcost;
   Total();
}
// function to copy billing address to shipping address
function CopyAddress() {
   if (document.order.same.checked) {
      document.order.shipto.value = document.order.billto.value;
   }
}
//global variable for error flag
var errfound = false;
//function to validate by length
function ValidLength(item, len) {
   return (item.length >= len);
}
//function to validate an email address
function ValidEmail(item) {
   if (!ValidLength(item, 5)) return false;
   if (item.indexOf ('@', 0) == -1) return false;
   return true;
}
// display an error alert
function error(elem, text) {
// abort if we already found an error
   if (errfound) return;
   window.alert(text);
   elem.select();
   elem.focus();
   errfound = true;
}
// main validation function
function Validate() {
   errfound = false;
   if (!ValidLength(document.order.name1.value,6))
      error(document.order.name1,"Invalid Name");
   if (!ValidLength(document.order.phone.value,10))
      error(document.order.phone,"Invalid Phone");
   if (!ValidLength(document.order.billto.value,30))
      error(document.order.billto,"Invalid Billing Address");
   if (!ValidLength(document.order.shipto.value,30))
      error(document.order.shipto,"Invalid Shipping Address");
   if (!ValidEmail(document.order.email.value))
      error(document.order.email, "Invalid Email Address");
   if (document.order.totalcost.value == "")
      error(document.order.qty1, "Please Order at least one item.");
   if (document.order.payby.selectedIndex != 1) {
      if (!ValidLength(document.order.creditno.value,2))
         error(document.order.creditno,"Invalid Credit/Check number");
   }
   return !errfound; /* true if there are no errors */
}
</SCRIPT>
</HEAD>
<BODY>
<H1>Order Form</H1>
<FORM NAME="order" onSubmit="return Validate();">
<B>Name:</B> <INPUT TYPE="text" NAME="name1" SIZE=20>
<B>Phone: </B><INPUT TYPE="text" NAME="phone" SIZE=15>
<B>E-mail address:</B><INPUT TYPE="text" NAME="email" SIZE=20><BR>
<B>Billing and Shipping Addresses:</B>
<INPUT TYPE="chECKBOX" NAME="same" onClick="CopyAddress();">
Ship to Billing Address
<BR>
<TEXTAREA NAME="billto" COLS=40 ROWS=4 onChange="CopyAddress();">
Enter your billing address here.
</TEXTAREA>
<TEXTAREA NAME="shipto" COLS=40 ROWS=4 onChange="CopyAddress();">
Enter your shipping address here.
</TEXTAREA>
<B>Products to Order:</B><BR>
Qty: <INPUT TYPE="TEXT" NAME="qty1" VALUE="0" SIZE=4
onChange = "UpdateCost(1, 40.00);">
Cost: <INPUT TYPE="TEXT" NAME="cost1" SIZE=6>
($40.00 ea) Fictional Spreadsheet 7.0 <BR>
Qty: <INPUT TYPE="TEXT" NAME="qty2" VALUE="0" SIZE=4
onChange = "UpdateCost(2, 69.95);">
Cost: <INPUT TYPE="TEXT" NAME="cost2" SIZE=6>
($69.95 ea) Fictional Word Processor 6.0<BR>
Qty: <INPUT TYPE="TEXT" NAME="qty3" VALUE="0" SIZE=4
onChange = "UpdateCost(3, 99.95);">
Cost: <INPUT TYPE="TEXT" NAME="cost3" SIZE=6>
($99.95 ea) Fictional Database 7.0 <BR>
Qty: <INPUT TYPE="TEXT" NAME="qty4" VALUE="0" SIZE=4
onChange = "UpdateCost(4, 4.95);">
Cost: <INPUT TYPE="TEXT" NAME="cost4" SIZE=6>
($4.95 ea) Instruction Booklet for the above <HR>
<B>Total Cost:</B>
<INPUT TYPE="TEXT" NAME="totalcost" SIZE=8><HR>
<B>Method of Payment</B>:
<SELECT NAME="payby">
<OPTION VALUE="check" SELECTED>Check or Money Order
<OPTION VALUE="cash">Cash or Cashier's Check
<OPTION VALUE="credit">Credit Card (specify number)
</SELECT><BR>
<B>Credit Card or Check Number:</B>:
<INPUT TYPE="TEXT" NAME="creditno" SIZE="20"><BR>
<INPUT TYPE="SUBMIT" NAME="submit" VALUE="Send Your Order">
<INPUT TYPE="RESET" VALUE="Start Over">
</FORM>
</BODY>
</HTML>

Tip
At this point, submitting the form won't do anything; however, you can still test the submission. Submitting will reload the current page and add the various data to the URL.

Workshop Wrap-Up

In this chapter, you built a fully functional ordering system for an imaginary company, which could easily be modified for use by a real company. In the process, you learned the following:

Next Steps

You should now know how to use JavaScript with forms. To move on, turn to one of the following chapters:

Q&A

Q:
Is there any way to force a form to be submitted automatically, without the user pressing a button?
A:
Yes. You can do this with the form.submit() method. However, this is bad manners; it's usually best to let the user know what's going on. Also note that due to security concerns, you can't do this with a form that uses the mailto action.
Q:
I am having problems trying to force a checkbox to be selected using the click method. Is there a way to solve this?
A:
Unfortunately, at least for many versions of Netscape, the only solution is not to use the click method. Fortunately, it's easy to do most things without it. For a checkbox, you can manipulate the checked property; for a button, you can call the same function as its onClick event handler.
Q:
If I use JavaScript to add validation and other features to my form, can users with non-JavaScript browsers still use the form?
A:
Yes, if you're careful. Be sure to use a SUBMIT button rather than the submit action. Also, because the CGI script may receive nonvalidated data, be sure to include validation in the CGI script. Non-JavaScript users will be able to use the form but won't receive instant feedback about their errors.
Q:
Can I add new form elements "on the fly," or change them-for example, change a text box into a password field?
A:
No. The form elements are set by the HTML code. There are ways to work around this, such as updating the form in a separate frame.
Q:
Is there any way to create a large number of text fields without dealing with different names for all of them?
A:
Yes. If you use the same name for several elements in the form, their objects will form an array. For example, if you defined 20 text fields with the name member, you could refer to them as member[0] through member[19]. Chapter 15, "Real-Life Examples III," uses this technique for score values in a game.
Q:
When validating an e-mail address, is there any way to be sure the address is valid, or that it is the user's address?
A:
No. This is a classic question about CGI; neither JavaScript nor CGI has a good solution. The only way to be sure an e-mail address is valid is to send information, such as a password, to the address; even then, you can't be sure users are entering their own addresses.
Q:
Why doesn't JavaScript recognize my form elements when I use a table to lay them out?
A:
JavaScript does not deal well with forms within tables when <TABLE> tags are nested. For now, the only solution is to avoid using nested tables.
Q:
Is there a way to place the cursor on a particular field when the form is loaded?
A:
Yes. You can use the field's focus() method to send the cursor there. The best way to do this is to use an onLoad event handler and add a slight delay with the setTimeout() method to allow the page to finish loading.