9.3 JavaScript and Client-Side Validation


In this section, we introduce the JavaScript scripting language as a client-side method for validation and other simple tasks. JavaScript isn't a full-fledged programming language like PHP: it can't connect to databases, it offers only limited interaction with certain system resources, and it can't do most tasks a web database application requires. However, JavaScript is good for interacting with a form and for controlling the display of data to the user.

Client-side validation with JavaScript is optional but has benefits, including faster response to the user than server-side validation, a reduction in web server load, and a reduction in network traffic. Also, unlike server-side validation, it can be implemented as interactive validation where errors are checked as they occur and field-by-field reporting where error messages are shown individually. However, validation in the client tier is unreliable: the user can bypass the validation through design, error, or misconfiguration of their web browser. For that reason, client-side validation should be used only to improve speed, reduce load, and add features, and never to replace server-side validation.

The client-side scripting language we use here is best known as JavaScript. However, in June 1998, the European Computer Manufacturers Association (ECMA) agreed to be responsible for the standard implementations of the scripting language by Microsoft, Netscape, and Sun. Accordingly, the real name of the language is now ECMA-Script, based on the standard ECMA-262. The most recent version of ECMA-262 is the third edition, dated December 1999. Netscape still use the name JavaScript, and JavaScript 1.5 is fully compatible with ECMA-262 Version 3.

The standard is available from http://www.ecma-international.org/publications/standards/ECMA-262.htm


Besides validation, there are many other common uses of JavaScript in web database applications including:

  • Simple interaction with form data. For example, JavaScript is often used to calculate values and display these in an input widget.

  • Enhancing user interactions by adding dynamic elements to a web page. Common features include pull-down menus, mouseover changes to the presentation (rollovers), and dialog boxes.

  • Customizing the browser and using information from the browser to enhance presentation.

Most of these techniques are oriented around events. An event is an action that can be trapped through JavaScript code, such as a mouse passing over an object, a window opening, or a user clicking on a button.

The next section introduces JavaScript through a simple example. After that, we show you the basics of JavaScript by contrasting and comparing it with PHP, and then we show you several more examples including a case study. However, this section isn't comprehensive and isn't aimed as a replacement for many of the excellent resources that are available; selected resources are listed in Appendix G.

9.3.1 Introducing JavaScript

Consider the short JavaScript validation example in Example 9-5.

Example 9-5. A simple JavaScript example to check if a form field is empty
<!DOCTYPE HTML PUBLIC                  "-//W3C//DTD HTML 4.01 Transitional//EN"                  "http://www.w3.org/TR/html401/loose.dtd"> <html> <head>   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">   <title>Simple JavaScript Example</title> <script type="text/javascript"> <!-- Hide the script from old browsers function containsblanks(s) {   for(var i = 0; i < s.value.length; i++)   {       var c = s.value.charAt(i);       if ((c == ' ') || (c == '\n') || (c == '\t'))       {           alert('The field must not contain whitespace');           return false;       }   }   return true; } // end hiding --> </script> </head> <body> <h1>Username Form</h1> <form onSubmit="return(containsblanks(document.userform.username));"                 method="POST" name="userform" action="test.php"> <input type="text" name="username" size=10> <input type="submit"> </form> </body> </html>

This example is designed to check if an optional username field contains whitespace and, if so, to show a dialog box containing an error message to the user. The dialog box is shown in Figure 9-1. The example contains a mixture of HTML and JavaScript, and almost all the JavaScript is encapsulated between the <script> and </script> tags in the <head> tag of the document.

Figure 9-1. The dialog box produced when whitespace is entered in the username field
figs/wda2_0901.gif


The JavaScript function containsBlanks( ) is called when the user submits the form. The function call is part of the form element:

<form onSubmit="return(containsblanks(document.userform.username));"  method="post" name="userform" action="test.php">

When the submission event occurs (when the user presses the Submit button or presses the Enter key while the cursor is in the text widget) the onSubmit event is triggered. In this case, the result is that the function containsblanks( ) is called with one parameter, document.userform.username. The object document refers to the document loaded in the browser window, the userform is the name of the form itself, and username is the name of the input widget within the form. The function call itself is wrapped in a return( ) expression. The overall result of executing containsblanks( ) is that if the function returns false, the form isn't submitted to the server; if the function returns true, the HTTP request proceeds as usual.

The function containsblanks( ) works as follows:

  • A for loop iterates through the characters entered by the user. The expression s.value.length refers to the length of the string value entered by the user into the username widget. The length property is one of the predefined properties of the value attribute of the <input> widget.

  • Each character in the string entered by the user is assigned to a character variable c using the expression s.value.charAt(i) to return the characters in the value entered by the user. The value attribute of the widget has an associated method charAt( ) that returns the value of the character at the position passed as a parameter. For example, if the user enters test in the widget, s.value.charAt(0) returns t, and s.value.charAt(1) returns e.

  • The if statement checks whether the current character is a space, a tab character, or a carriage return. If so, the alert( ) method is called with an error string as a parameter. The alert( ) method presents a dialog box in the browser that shows the error message and has an OK button, as shown in Figure 9-1. When the user clicks OK, the function returns false, and the submission process stops.

  • If the string doesn't contain any whitespace, the function containsblanks( ) returns true, and the form submits as usual.

HTML comment tags are included inside the <script> tags and surround the JavaScript script. This is good practice, because if JavaScript is disabled or the user has an old browser that knows nothing about scripts, the comments hide the script from a potentially confused browser. An old browser happily displays the HTML page as usual. In addition, an old browser or one that has JavaScript turned off will ignore the onSubmit event handler in the form element.

9.3.2 JavaScript and PHP

The syntax of JavaScript is similar to PHP and to other languages such as C and Java. Table 9-2 compares some of the basic features of PHP and JavaScript that we used in Example 9-5 and others that are used later in this chapter. The key differences are that JavaScript variables aren't prefixed with a dollar sign, local variables must be declared in JavaScript, different open and close script tags are used, and string concatenation in JavaScript uses a plus sign and PHP uses a period. Other than that, the languages are very similar when used for basic tasks.

Table 9-2. The language basics in PHP and JavaScript

Language component

PHP

JavaScript

Open and close script tags

<?php ?>

<script type="text/javascript"> </script> or <% %>

Block statement

{ }

{ }

Multi-line comment

/* hello */

/* hello */

Single-line comment

// hello

// hello

Constant declaration

define("z", 1);

const a = 1;

Variable declaration

Not required

Required for local variables, var a = 0;

Variable assignment

$a = 0;

a = 0;

Assignment shortcut style

$a += 5;

a += 5;

Variable typing

At runtime

At runtime

Statement terminator

;

; or the end-of-line

Equality value testing

Double-equals, ==

Double-equals, ==

Equality type and value testing

Triple-equals, ===

Triple equals, ===

Inequality testing

!=

!=

Strings

"string" 'string'

"string" 'string'

String constants

\n and \t

\n and \t

String concatenation

$a = $b . $c;

$a = $b + $c;

Boolean values

true false

true false

Logical AND

&&

&&

Logical OR

||

||

Logical NOT

!

!


9.3.2.1 Generating output

In PHP, output to the browser is generated using the print or printf statements, or by using a template and template methods as discussed in Chapter 7. In JavaScript, there are several different ways output can be produced including writing output to the browser window as a document is created, creating dialog boxes, updating values in form widgets, and creating new windows.

To write output to a window, the writeln( ) method can be used:

<script type="text/javascript">   document.writeln("Hello, world."); </script>

The document object refers to the document that is displayed in the browser window, and writeln( ) is a method associated with that document. You can write to a document only as it's created, you can't use this method to write text to the document after it's been rendered in the browser. The basic objects and methods are discussed later in this section.

In Example 9-5, a dialog box with an OK button is created with the alert( ) method. For example, you can pop up a dialog box when the user clicks on a button:

<form action="test.php"> <input type="button" value="Pop a box" onclick="alert('Pop!');"> </form>

The onclick attribute causes the box to appear when the user clicks on the button.

It's also possible to create dialog boxes using the confirm( ) method that displays both an OK and Cancel button:

<script type="text/javascript"> if (confirm("Are you sure?"))   alert("Great!"); else   alert("What a pity!"); </script>

The confirm( ) method returns true when the user clicks Ok and false otherwise.

Another approach to producing output is to write to the browser window status line. However, this isn't a very effective mechanism: the status bar may be hidden or disabled, and it's easy to overlook messages displayed at the base of the window. Yet another approach is to create a new fully-featured non-dialog browser window, which we discuss later in this chapter. The final approach is to update values in input widgets, an approach we use later in our examples.

9.3.2.2 Loops and conditionals

Loops and conditionals are almost the same in both languages. As discussed in Chapter 2, PHP has the for, while, foreach, and do...while loops, and the if and switch conditionals. JavaScript has the for, while, do...while, and for...in loops, and the if and switch statements. The continue and break statements are available in both languages.

The for, while, and do...while loops are the same in PHP and JavaScript, with the exception that in JavaScript it's possible to declare a variable in a for loop with the var statement; an example is shown in Example 9-5. JavaScript also has the for...in statement which allows you to iterate through properties of objects, while PHP has the foreach statement for iterating through elements in arrays. An example with the for...in statement is presented later in this section.

9.3.2.3 Functions

Functions are similar in PHP and JavaScript. Consider the following JavaScript example:

function bold(string) {   document.writeln("<b>" + string + "</b>\n"); }

When called with the function call bold("this is bold"), the function prints the string <b>this is bold</b> as part of the document. Similarly to PHP, functions are declared with the statement function, parameters are listed in brackets and separated by commas, and the function body is surrounded by curly braces. Functions can optionally return values using the return statement, which behaves identically in PHP and JavaScript.

Variables that are declared within a function are local to that function. Local variables must be declared using the var statement as in the following example:

function count( ) {   var x=1;   while (x<6)   {     document.writeln(x + " ");     x++;   } }

Variables that are used or declared outside functions are global variables. Declaring globals with var is optional: as in PHP, they can be declared implicitly by assigning values to them. However, unlike PHP, global variables in JavaScript are accessible everywhere in the current document; global variables are not declared in functions using the global keyword.

9.3.2.4 Debugging JavaScript

JavaScript has two types of errors that report messages: load-time and run-time errors. Load-time errors are sometimes reported by the user agent before it runs the JavaScript, and you'll be shown a warning box that details the error, its line number, and the code fragment itself. Run-time errors occur when a code fragment is running and, again, a warning box is sometimes displayed with the line number of the code that caused the error.

The inconsistent nature of error reporting can be annoying: often, you'll get no messages at all but the script won't run. However, in many browsers (including Mozilla and Netscape), you can get more detailed error information by typing javascript: in the Location box and pressing Enter. The JavaScript console that pops up lists all errors that have occurred since the browser began running. You can remove old messages by clicking on the Clear button that's shown at the top of the console window; periodically doing this is a good way to avoid confusion about which errors are applicable to what.

Errors can also be annoying because they are often platform- or browser-dependent and change from one release to the next. Complex JavaScript adds a thicker client to a web database application, and this may reveal differences between browser applications, browser versions, and different platforms.

If complex JavaScript is required or desired, make sure it's tested on all the popular platforms with the popular browser products and versions. However, we recommend that JavaScript be kept simple: complex tasks should be left to PHP scripts, and you should ensure that user interfaces function correctly even if JavaScript is faulty or disabled.

9.3.2.5 Objects

Objects associated with the browser, windows, and the document are accessible in JavaScript. For example, in Example 9-5, the form object and its child (an input text widget) are accessed and used. Historically, the definition of these objects (and the events, properties, and methods described in the next sections) was part of the JavaScript standard and they were loosely known as the Navigator objects . This has now changed, and the objects are defined as part of the Document Object Model (DOM).

In this section, we informally describe the objects and properties that are accessible from within JavaScript, and avoid the details of DOM. However, the complete specification is accessible at http://www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109/.

The window object is the top of the DOM hierarchy, and it contains the toolbars and menus of the browser, as well as the document and its sub-components. The hierarchy of the objects that descend from window is shown in Figure 9-2.

Figure 9-2. The hierarchy of window objects in the DOM
figs/wda2_0902.gif


In JavaScript code, the document object can be referenced using the same notation as in PHP's object-oriented model using window.document, or just document for short (because there is only one window when you're not using frames, which we don't in this book).

Each of the objects in the hierarchy has properties and methods, and creates events. Properties are characteristics that describe the appearance of the object, while the methods are its behaviors. Events are actions that the object can act on such as mouse clicks or key presses. Events are discussed in the next section, and methods and properties in the following section. However, selected events, methods, and properties are used in examples here.

Consider a document that contains a form that has a text input widget and a submit button:

<form name="custform" method="GET" action="cust.php"> Surname: <input type="text" name="surname"> <br><input type="submit" name="submit"> </form>

The text widget is accessible using the names associated with the objects in the hierarchy. In this example, the form widget has the attribute name="custform", and the input widget has the attribute name="surname". You can therefore reference the text widget's value as document.custform.surname.value. The value is a property of the text input widget and it contains the data the user has entered, or it can be assigned a value to modify the data that's shown in the widget.

The value could be output when an onchange event is triggered in the widget itself:

<input type="text" name="surname"    onchange="alert('You entered ' +  document.custform.surname.value);">

The browser automatically generates an onchange event when the text in the widget changes; we explain events more later.

Alternatively, you could output the value when the submission process itself occurs:

<form name="custform" method="GET" action="cust.php"    onsubmit="alert('You entered ' +  document.custform.surname.value);">

Another way to access the properties of an object is to access the DOM element array. For example, the value of the text input widget in the custform can be referenced as:

document.forms[0].elements[0].value

The notation forms[0] means the first form in the document, and elements[0] means the first element of that form. You can iterate through all properties in an object using the for...in statement that we introduced previously. For example, to show the names of all elements in the form, use:

for (o in document.custform)    string += o + ' '; alert("Here are the elements: " + string);

The loop assigns each element in document.custform in turn to the variable o, and then o is appended to string. If you include this fragment in a document containing the custform, you may be surprised that it outputs not just the surname widget and the submit button, but more than 50 properties. Some of these are discussed in the next section.

Some browsers are fussier than others. For example, Microsoft's Internet Explorer complains when you reference an object before it's defined. This means that you can't reference a form earlier in your HTML source than where the form is actually declared. You'll find that these kinds of issues make developing complex or portable code difficult. As we've already discussed, we recommend you use JavaScript for simple tasks and leave the complex ones to PHP on the server side.

In JavaScript, there are also several other pre-defined objects. These include core objects such as Array, RegExp, Date, and Math that are discussed in detail in the Core JavaScript Guide available from http://devedge.netscape.com/library/manuals/.

9.3.2.6 Events

Events are triggered by both the user agent (usually a web browser), and the user working with the document and browser. These events are useful triggers for JavaScript actions. For example, a function might be called as a page loads, when the user presses the submit button, when a form field changes, or when the mouse passes over a document element. Examples using many of these events are included in later examples in this chapter.

The key events that can be trapped and handled by JavaScript are as follows:


onblur

When a user removes focus from form elements or a window. This occurs when the user presses the Tab key to move to the next widget, clicks on another widget or window, or carries out some other action that takes the focus away from the current window or widget.


onchange

When a select, text, or textarea input loses focus and has been modified since it gained focus.


onclick

When a pointing device (usually a left mouse button) clicks on an area, button, checkbox, hypertext link, radio button, reset, or submit.


onfocus

When a user brings focus to form elements or a window, normally by clicking in it.


onload

When a user agent finishes loading a window (or all frames in a frameset).


onmouseout

When the pointing device moves out from an element, area, or hypertext link.


onmouseover

When the pointing device moves over an element, area, or hypertext link.


onreset

When a form is reset.


onsubmit

When a form is submitted.


onunload

When the user exits a page, that is, when the user agent unloads the document from the window. For example, this happens when a new page is loaded, a browser window or tab is closed, or the browser program ends.

We have omitted other events related to key presses and text selection, as well as other types of mouse clicks and movements. These are detailed in the HTML 4.01 documentation at http://www.w3.org/TR/html4/interact/scripts.html. and in the DOM documentation listed in the previous section.

9.3.2.7 Methods and properties

The window, document, form, and input element objects have properties and methods that are commonly accessed and used in validation tasks. This section lists selected methods and properties, and examples later in this chapter show many of these used in scripts. Images, tables, the document body, document styles, and frames also have their own methods and properties, but we don't discuss these here. The complete list of objects, methods, and properties can be found at http://www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109/ecma-script-binding.html.

The navigator object is outside of the window hierarchy we showed in Figure 9-2 and the standards. However, it is useful because it describes the browser environment. Its properties include:


platform

The operating system.


userAgent

The same information as sent in the HTTP request that includes the user agent name and version.

The window object properties and methods include:


location.href

The URL in the location box.


name

The window's name. Can be used to retrieve or set the window name. For example, window.name = "Hello!" sets the title to Hello!.


locationbar.visible

Determines whether the location bar is visible. It can be set to true or false, making the location bar visible or hidden respectively.


menubar.visible

Determines whether the menu bar is visible. It can be set to true or false, making the menu bar visible or hidden respectively.


personalbar.visible

Determines whether the personal or directories bar is visible. It can be set to true or false, making the personal bar visible or hidden respectively.


scrollbars.visible

Determines whether the horizontal and vertical scroll bars are visible. It can be set to true or false, making the scroll bars visible or hidden respectively.


statusbar.visible

Determines whether the status bar is visible. It can be set to true or false, making the status bar visible or hidden respectively.


toolbar.visible

Determines whether the toolbar is visible. It can be set to true or false, making the toolbar visible or hidden respectively. It can be set or unset only before the window is opened.


status

The text displayed in the status bar at the base of window. This can be set to display a message to the user.


alert( )

Shows a dialog box with an OK button, and takes text as a parameter.


back( )

Causes the user agent to return to the previous resource in its history list. This has the same effect as pressing the Back button in the web browser.


close( )

Closes the current window.


confirm( )

Shows a dialog box with OK and Cancel buttons, and takes text as a parameter. Returns true when the user presses OK and false otherwise.


forward( )

Causes the user agent to go forward to the next resource in its history list. This has the same effect as pressing the Forward button in the web browser.


open( )

Opens a new window, taking a URL, name, and features as parameters.


print( )

Sends the contents of the window to a printer.


prompt( )

Shows a dialog box with an input widget and OK and Cancel buttons. Takes a text question to display, and optional default text to display in the input widget. Returns true when the user presses OK and false otherwise.

The document object properties and methods include:


lastModified

The date the resource was last modified.


title

The text contained in the <title> tag of the document.


URL

The URL of the current document.


write( ) and writeln( )

Writes text to the current document during its creation. The latter adds a carriage return character to the string.

The form objects in a document include the following properties and methods:


name

The value of the name attribute.


action

The value of the action attribute.


method

The value of the method attribute (GET or POST).


submit( )

Sends the form to the server.


reset( )

Clears all user-entered input from the form. If the form was shown with pre-filled values, it is reset to those values. If the form was initially empty, it is reset to empty.

The form elements select, textarea, input, and button have common methods and properties that include:


type

The type of input as defined by the type attribute in the form element.


value

The value contained or selected in the form element.


value.length

The length of the value contained or selected in the form element.


name

The value of the name attribute in the form element.


focus( )

Brings the focus to the form element (not used with button elements).


blur( )

Removes the focus from the form element (not used with button elements).


select( )

Selects (usually by highlighting) the text in an input or textarea element (not used with select or button elements).


value.charAt( )

Returns the character in the value at the position of the integer parameter. For example, value.charAt(0) returns the first character in the value.

9.3.3 JavaScript Examples

The short examples in this section implement simple, common, and useful JavaScript web database application features that use the techniques we have discussed so far. These include:

  • Checking if two passwords are the same

  • Mouse rollovers, where an image is changed to highlight an option as the mouse cursor passes over it

  • Calculating and updating form fields based on user changes to data

  • Interacting with the web browser and windows to trigger events and manipulate presentation

  • Detecting the browser application and version

  • Drop-down menus that load a new URL into the current window

9.3.3.1 A password form validation function

Example 9-6 is an example of JavaScript validation that checks whether a password is the same when the user enters it twice. The validation is interactive: an onchange event is trapped for the two password widgets, formPassword1 and formPassword2, and the function thesame( ) is called whenever the user changes the data in a widget and then leaves it. The error reporting is field-by-field.

Example 9-6. Using JavaScript for interactive validation of password fields
<!DOCTYPE HTML PUBLIC               "-//W3C//DTD HTML 4.0 Transitional//EN"               "http://www.w3.org/TR/html4/loose.dtd"> <html> <head>   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">   <title>Password Validation</title> <script type="text/javascript"> <!-- Hide the script function thesame(value1, value2) {   if (((value1 != null) || (value1 != ""))        && value2 != "" && value1 != value2)   {        alert("The passwords must be identical.");        return (false);   }   return (true); } // end hiding --> </script> </head> <body> <h1>Username Form</h1> <form method="post" action="test.php" name="userForm"> <br>Username: <input type="text" name="userName" size=10> <br>Password:   <input type="password" name="formPassword1" size=10   onchange="thesame(document.userForm.formPassword1.value,                     document.userForm.formPassword2.value);"> <br>Re-enter password:   <input type="password" name="formPassword2" size=10   onchange="thesame(document.userForm.formPassword2.value,                     document.userForm.formPassword1.value);"> <br><input type="submit" value="SUBMIT"> </form> </body> </html>

The function thesame( ) checks if the current widget contains data. If it does, and the other password widget also contains data, the data in the two widgets is compared. If the data in the widgets is different, an error message is shown to the user. It's necessary to test whether both widgets actually contain data in interactive validation; without this check, the function annoyingly displays an error before the user has the opportunity to enter data into both widgets.

9.3.3.2 Rollover presentation with mouseover events

Example 9-7 shows a basic implementation of the common rollover feature used in many web applications.

Example 9-7. mouseover example with JavaScript
<!DOCTYPE HTML PUBLIC                "-//W3C//DTD HTML 4.01 Transitional//EN"               "http://www.w3.org/TR/html401/loose.dtd"> <html> <head>   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">   <title>MouseOver Example</title> </head> <body bgcolor="#ffffff"> <a href="add_to_cart.php" onmouseout="cart.src='/books/2/581/1/html/2/cart_off.jpg'"                           onmouseover="cart.src='/books/2/581/1/html/2/cart_on.jpg'"> <img src="/books/2/581/1/html/2/cart_off.jpg" border=0 name="cart" alt="cart picture"></a> </body> </html>

When the page is first loaded, an un-highlighted image of a shopping cart is shown; the image is used in the front page of the winestore in Chapter 16. The image is loaded with the HTML fragment:

<img src="/books/2/581/1/html/2/cart_off.jpg" border=0 name="cart">

The only difference to the usual approach of loading images is that the <img> tag has the attribute name="cart".

If the mouse passes over the cart image, an onmouseover event is triggered, and the JavaScript action carried out is:

onmouseover="cart.src='/books/2/581/1/html/2/cart_on.jpg'"

The event handler changes the value of the src attribute of the <img> tag with the name="cart". The result is that a new highlighted image is loaded to replace the un-highlighted image. In the case of our winestore, a shopping cart with a blue foreground is shown.

When the mouse leaves the image region, the onmouseout event is generated and handled with the following JavaScript fragment:

onmouseout="cart.src='/books/2/581/1/html/2/cart_off.jpg'"

This restores the original image. The impression to the user is that the cart element is highlighted as the user focuses on the element.

Rollovers are straightforward to develop and the approach we've shown you works in all graphical browsers. You can even use the same technique to highlight menu options, and to produce pop-up and pull-down menus.

9.3.3.3 Prefilling form data with JavaScript calculations

Another common use of JavaScript is to pre-fill a form with data from a calculation. Example 9-8 shows how data can be managed and updated in a shopping cart.

When the user changes the quantity of a wine he intends to purchase, an onchange event is generated. This change event is handled by the update( ) function, which modifies the value attribute of the total widget, showing the new total cost to the user. The new value shown to the user is calculated by multiplying together the quantity.value and the unit.value. Of course, as in all web database applications, the values and mathematics should be rechecked at the server when the form is submitted to the server.

Example 9-8. Using JavaScript to dynamically update values of form widgets
<!DOCTYPE HTML PUBLIC               "-//W3C//DTD HTML 4.01 Transitional//EN"               "http://www.w3.org/TR/html401/loose.dtd"> <html> <head>   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">   <title>Dynamic Form Update Example</title> </head> <body> <h1>Your Shopping Cart</h1> <form method="get" action="test.php"> <table border="0" width="100%" cellpadding="0" cellspacing="5"> <tr>   <td>Quantity </td>   <td>Wine</td>   <td>Unit Price</td>   <td>Total</td> </tr> <tr>   <td><input type="text" name="quantity" value="1" size=3        onchange="total.value = unit.value * quantity.value;">   <td>1997 Anderson and Sons Wines Belcombe Grenache</td>   <td>$<input type="text" value="17.29" name="unit" readonly></td>   <td>$<input type="text" value="17.29" name="total"          align="right" readonly></td> </tr> </table> <input type="submit" value="Purchase Wines"> </form> </body> </html>

9.3.3.4 Interacting with the web browser

Example 9-9 shows four examples of handlers for buttons that use the methods defined for the window object. The method window.close( ) closes the focused window, window.print( ) shows the print dialog window, window.back( ) goes back one page, and window.open( ) opens a new browser window.

Example 9-9. Closing and opening windows with JavaScript, printing the current page, and adding a Back button to a form
<!DOCTYPE HTML PUBLIC               "-//W3C//DTD HTML 4.01 Transitional//EN"               "http://www.w3.org/TR/html401/loose.dtd"> <html> <head>   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">   <title>Playing with the Browser and Windows</title> </head> <body> <h1>Playing with the Browser and Windows</h1> <form action="example.9-6.php"> <input type="button" value="Close Window" onClick="window.close( );"> <br><input type="button" value="Print Window" onClick="window.print( );"> <br><input type="button" value="Go Back" onclick="window.back( );"> <br><input type="button" value="Visit the book site"   onClick="window.open('http://www.webdatabasebook.com/','BookSite',   'toolbar=yes,location=yes,menubar=yes,directories=yes,scrollbars=yes,   resizable=yes');"> </form> </body> </html>

Only window.open( ) is complex. The first parameter is the URL to request in the new window, the second is a title, and the third is a set of properties of the new window. Without the list of properties that are included, the default new window has no Location box, no toolbars, no scrollbars, and can't be resized.

9.3.3.5 Which browser is the user using?

As discussed previously, even simple JavaScript sometimes highlights annoying differences in the way browsers support standard features. Indeed, even different versions of the same browsers support different JavaScript features from the same version of the standard.

Example 9-10 shows how the browser application name and version can be detected with both JavaScript and PHP. If a JavaScript script requires customization for a particular product, if statements can carry out actions in different ways. Another common approach in JavaScript-intensive web database applications is to write two sites: one that uses Internet Explorer JavaScript (known as Jscript), and another that uses Netscape Navigator or Mozilla JavaScript. However, as we recommended earlier, complex JavaScript is often best avoided in favor of server-side scripts.

Example 9-10. Which browser is the user using?
<!DOCTYPE HTML PUBLIC               "-//W3C//DTD HTML 4.01 Transitional//EN"               "http://www.w3.org/TR/html401/loose.dtd"> <html> <head>   <title>Playing with the Browser and Windows</title>   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> </head> <body> <script type="text/javascript"> <!-- Hide the script from old browsers   alert("You are using " + navigator.userAgent); // end the hiding --> </script> This page should pop up a box if you have a JavaScript-capable and enabled browser. <br>But, using PHP, we can tell you that you're using the <?php print $_SERVER["HTTP_USER_AGENT"]; ?> browser. </body> </html>

9.3.3.6 Drop-down menus

A common use of JavaScript is to automatically load a new page when a user selects a menu option from a drop-down list. Example 9-11 shows how to do this using a select widget and its properties. The JavaScript in the body of the document is straightforward: when the user changes their selection of menu item, an onchange event is triggered, and the loadNewPage( ) function is called.

Example 9-11. Drop-down menus that load a new URL
<!DOCTYPE HTML PUBLIC               "-//W3C//DTD HTML 4.01 Transitional//EN"               "http://www.w3.org/TR/html401/loose.dtd"> <html> <head>   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">   <title>Menu Example</title> <script type="text/javascript"> <!-- function loadNewPage( ) {   var listItem = document.menuForm.newPage.selectedIndex;   var newPage = document.menuForm.newPage.options[listItem].value;   location.href = newPage; } //--> </script> <body> Where do you want to go now? <br><form method="GET" action="menus.html" name="menuForm"> <select name="newPage" onchange="loadNewPage( );"> <option value="menus.html">This page <option value="http://www.webdatabasebook.com/">The book web site <option value="http://www.oreilly.com/">O'Reilly and Associates <option value="http://www.hughwilliams.com/">Hugh's homepage <option value="http://www.mds.rmit.edu.au/~dave/">Dave's homepage </select> </form> </body> </html>

The form in Example 9-11 has a name attribute of menuForm and the select list has a name attribute of newPage. Therefore, the list is referenced as document.menuForm.newPage. The loadNewPage( ) function references the list to load the new page in three steps:

  1. The local variable listItem is assigned the ordinal number of the selected value from the list. To discover which item is selected, the selectedIndex property of the newPage <select> is inspected. For example, if the first item is selected then the value is 0.

  2. The value of the selected item (which contains the new URL) is determined by accessing the options array property of the list and retrieving the value of the element listItem. This value is stored in the local variable newPage. For example, if the first item in the list is selected listItem is 0, the value is the URL http://www.webdatabasebook.com/, and newPage is set to that value.

  3. To load the new URL in the current window, the location.href property is set to newPage. This causes the new document to load.

9.3.4 Case Study: A Generic JavaScript Validation Function

The example in this section shows how JavaScript can be used as a validation tool across multiple HTML pages or templates. An example of errors produced by applying the techniques described in this section to customer validation is shown in Figure 9-3. We show you the JavaScript code, the PEAR IT template, and the PHP code in this section.

Figure 9-3. A dialog box showing an error produced by the JavaScript validation function
figs/wda2_0903.gif


9.3.4.1 The JavaScript validation script

The general-purpose verify( ) function for post-validation and field-by-field error reporting is shown in Example 9-12. The code is stored in the file example.9-12.js and is designed to be added to a template, such as the phonebook template developed in Chapter 8. Later in this section, we show you how to add it to a customer details template that has diverse validation needs.

By storing JavaScript code in its own file, it can be reused across multiple HTML pages or templates. To do this, instead of including code between the <script> and </script> tags, you add a src attribute to the <script> element that specifies the file that contains the JavaScript code. For example, to load the code in Example 9-12 into a document or template, you use:

<script type="text/javascript" src="/books/2/581/1/html/2/example.9-12.js"> </script>

This approach saves cutting and pasting the code into more than one file, and avoids the need to update several pages when the script changes. It also has the additional advantage of reducing network traffic if the user has a web browser cache, because a copy of the script can be reused in multiple HTML pages without retrieving it again from the web server.

Example 9-12. A general-purpose JavaScript form validation function
// A utility function that returns true if a string contains only // whitespace characters. function isblank(e) {   if (e.value == null || e.value == "")     return true;   for(var i = 0; i < e.value.length; i++)   {      var c = e.value.charAt(i);      if ((c != ' ') &&          (c != '\n') &&          (c != '\t'))         return false;   }   return true; } // Checks if an optional field is blank function checkblank(e) {   if (isblank(e))   {     alert("The field " + e.description + " must be filled in.");     return false;   }   return true; } // Checks if a field is numeric. // If the optional min property is set, it checks it is greater than // its value // If the optional max property is set, it checks it is less than // its value function checknumber(e) {   var v = parseFloat(e.value);   if (isNaN(v))   {     alert("The field " + e.description + " must be a number");     return false;   }   if ((e.minNumber != null) && (v < e.minNumber))   {     alert("The field " + e.description +           " must be greater than or equal to " + e.minNumber);     return false;   }   if (e.maxNumber != null && v > e.maxNumber)   {     alert("The field " + e.description +           " must be less than or equal to " + e.maxNumber);     return false;   }   return true; } // Checks if a field looks like a date in the 99/99/9999 format function checkdate(e) {   var slashCount = 0;   if (e.value.length != 10)   {     alert(" The field " + e.description +           " must have the format 99/99/9999" +           " and be 10 characters in length");     return false;   }   for(var j = 0; j < e.value.length; j++)   {     var c = e.value.charAt(j);     if ((c == '/'))        slashCount++;     if (c != '/' && (c < '0' || c > '9'))     {       alert(" The field " + e.description +             " can contain only numbers and forward-slashes");       return false;     }   }   if (slashCount != 2)   {     alert(" The field " + e.description +           " must have the format 99/99/9999");     return false;   }   return true; } // Checks if a field contains any whitespace function checkwhitespace(e) {   var seenAt = false;   for(var j = 0; j < e.value.length; j++)   {      var c = e.value.charAt(j);      if ((c == ' ') || (c == '\n') || (c == '\t'))      {        alert("The field " + e.description +              " must not contain whitespace");        return false;      }   }   return true; } // Now check for fields that are supposed to be emails. // Only checks that there's one @ symbol and no whitespace function checkemail(e) {   var seenAt = false;   for(var j = 0; j < e.value.length; j++)   {     var c = e.value.charAt(j);     if ((c == ' ') || (c == '\n') || (c == '\t'))     {       alert("The field " + e.description +              " must not contain whitespace");       return false;     }     if ((c == '@') && (seenAt == true))     {       alert("The field " + e.description + " must contain only one @");       return false;     }     if ((c == '@'))       seenAt = true;   }   if (seenAt == false)   {     alert("The field " + e.description + " must contain one @");     return false;   }   return true; } // This is the function that performs <form> validation. // It is invoked from the onSubmit( ) event handler. // The handler should return whatever value this function // returns. function verify(f) {   // Loop through the elements of the form, looking for all   // text and textarea elements. Report errors using a post validation,   // field-by-field approach   for(var i = 0; i < f.length; i++)   {      var e = f.elements[i];      if (((e.type == "text") || (e.type == "textarea")))      {         // first check if the field is empty and shouldn't be         if (!e.isOptional && !checkblank(e))           return false;         // Now check for fields that are supposed to be numeric.         if (!isblank(e) && e.isNumeric && !checknumber(e))           return false;         // Now check for fields that are supposed to be dates         if (!isblank(e) && e.isDate && !checkdate(e))           return false;         // Now check for fields that are supposed to be emails         if (!isblank(e) && e.isEmail && !checkemail(e))           return false;         // Now check for fields that are supposed         // not to have whitespace         if (!isblank(e) && e.hasNospaces && !checkwhitespace(e))           return false;      } // if (type is text or textarea)   } // for each character in field   // There were no errors if we got this far   return true; }

Example 9-12 contains several functions and the main function is the last one in the file, verify( ). The verify( ) function is called when a form is submitted, and it expects the form object to be passed to it as a parameter. The function iterates through the elements in the form and carries out validation checks on each field, depending on what properties you set for that field. If any check fails, the function returns false. If all checks succeed, the function returns true. We show you how to call the function and set the element properties later.

The first fragment of the verify( ) function is as follows:

function verify(f) {   // Loop through the elements of the form, looking for all   // text and textarea elements. Report errors using a post validation,   // field-by-field approach   for(var i = 0; i < f.length; i++)   {      var e = f.elements[i];

A form object f is expected as a parameter. The for loop iterates through each element object in f. The first element is numbered 0 and the total elements in the form is stored in the property f.length. As discussed previously, the element objects are stored in the elements array and so, for example, f.elements[0] is the object representation of the first element in f. For compactness in the code, with each iteration of the loop, we assign the current element object to the local variable e.

The next fragment in verify( ) checks whether the current input element is of type text or textarea:

if (((e.type == "text") || (e.type == "textarea"))) {

We've only written validation functions for these types of element, and we leave it to you to extend this further to meet your needs.

The remainder of the verify( ) function tests different properties of the current element and calls functions to validate it. For example, the following fragment tests if the element contains a value (that is, it's not blank), if the isNumeric property is set, and if the value is not a number:

// Now check for fields that are supposed to be numeric. if (!isblank(e) && e.isNumeric && !checknumber(e))   return false;

The result of this check is that if the element isn't blank and is supposed to be numeric and isn't a number, the function returns false. In the same way as PHP's short-circuit evaluation discussed in Chapter 2, the second and subsequent tests in the if expression are only carried out if all preceding tests are true. The isblank( ) and checknumber( ) functions are validation functions in Example 9-12, and the isNumeric property is a user defined property that we discuss later.

There are several functions in Example 9-12 that each begin with the prefix check. In addition to testing if a mandatory field is blank and if a field is numeric, these check whether mandatory fields have data in them, dates are in a reasonable format, email addresses look plausible, and whether there's whitespace within a value. As an example, we discuss the checkdate( ) function next; we don't discuss the others in detail but they use the same ideas and validation steps.

The checkdate( ) functions perform very simple date format checking: it tests if a date has the format 99/99/9999 where 9 is a digit. More explicitly, it checks that the value is exactly 10 characters in length, contains only forward slashes and digits, and has only two forward slashes. It doesn't check the ordering of the characters, nor the validity of the date by the calendar. It's therefore a simple first step in validation: if the check succeeds, there's more chance it'll pass the more detailed server-side validation that occurs after the form is submitted. The checkdate( ) function returns true if validation succeeds and false otherwise.

The checkdate( ) function begins as follows:

// Checks if a field looks like a date in the 99/99/9999 format function checkdate(e) {   var slashCount = 0;   if (e.value.length != 10)   {     alert(" The field " + e.description +           " must have the format 99/99/9999" +           " and be 10 characters in length");     return false;   }

It expects a form element e as a parameter. The local variable slashCount is used later to count the number of forward slashes. The first test checks if the value is ten characters in length and, if not, it shows an error dialog and the function returns false. The description property of the element is set before the verify( ) function is called and we show you this later.

The next fragment is as follows:

  for(var j = 0; j < e.value.length; j++)   {     var c = e.value.charAt(j);

The for loop iterates through each character of the value in the element; the first element is 0 and the last is determined from the property e.value.length. For compactness in the later code, we store the current character in the local variable c by retrieving is using the built-in charAt( ) method discussed previously.

The body of the loop has two straightforward steps. First, if the current character is a forward slash, we increment the counter:

if ((c == '/'))   slashCount++;

Second, if the current character isn't a forward slash and isn't a digit we pop up an error dialog and the function returns false:

  if (c != '/' && (c < '0' || c > '9'))   {     alert(" The field " + e.description +           " can contain only numbers and forward-slashes");     return false;   } }

If the execution of the function makes it to the next fragment only digits and forward slashes have been encountered in the value. Now, we check whether there were two forward slashes:

  if (slashCount != 2)   {     alert(" The field " + e.description +           " must have the format 99/99/9999");     return false;   }   return true; }

If the check fails, we pop up an error dialog and return false. If all checks have succeeded the value looks like a date and the function returns true.

9.3.4.2 Using the JavaScript validation function

To use the verify( ) function, you call it from the onsubmit handler of a form. For example, suppose you have authored the customer details input form that's shown in Figure 9-4.

Figure 9-4. A customer form with JavaScript validation
figs/wda2_0904.gif


The form requires users to provide a first name, surname, address, email address, date of birth, and salary. For this form, the onsubmit handler that's used is as follows:

<form action="test.php" method="post" name="custform" onsubmit="document.custform.firstname.hasNospaces = true; document.custform.firstname.description = 'First Name'; document.custform.surname.description = 'Surname'; document.custform.address.description = 'Address'; document.custform.email.description = 'Email'; document.custform.email.isEmail = true; document.custform.dob.isDate = true; document.custform.dob.description = 'Date of Birth (99/99/9999)'; document.custform.salary.description = 'Salary'; document.custform.salary.isNumeric = true; document.custform.salary.minNumber = 1; document.custform.salary.maxNumber = 1000000; document.custform.salary.hasNospaces = true; return verify(document.custform);">

This code fragment creates and sets properties for each form element. These are properties that we've created (and not part of JavaScript itself), and each is used by some part of our verify( ) function. For example, when validation fails, the validation functions show error messages that inform the user which field contains the error. To pass this string to the validation function, we create a description property for all elements that are validated. For instance, the fragment:

document.custform.email.description = 'Email';

sets the description property of the email input element to 'Email'. As shown in the previous section, this description is displayed in a dialog box when an error occurs.

To control validation, you set a property that triggers a validation function in the verify( ) function. For example, if you want an element to be validated using the checkdate( ) function we described previously, you set the isDate property to be true:

document.custform.dob.isDate = true;

The verify( ) function inspects this property and, because it's true, it calls the checkdate( ) function to validate the dob field.

The other properties we've set up can be used to trigger other types of validation. You can set are isEmail (for an email address), hasNoSpaces (if an element should not contain whitespace), isNumeric (for integers), minNumber (a minimum value for an isNumeric element), maxNumber (a maximum value for an isNumeric element), and isOptional (for an element that can be left blank).

9.3.4.3 The PHP and template components

To complete our JavaScript validation case study, Example 9-13 and Example 9-14 show the PHP script and template respectively that create the customer details form.

Example 9-13 is a variation of Example 8-7 (which displays the phonebook form) with three differences. The first is that it displays several more elements than the phonebook script. The second is that it sets a new SUBMITACTION placeholder in the template to the JavaScript code fragment discussed in the previous section. The final difference is that it also sets the name of the form into a new placeholder, FORMNAME.

Example 9-13. The PHP script to produce the customer details form
<?php require 'db.inc'; require_once "HTML/Template/ITX.php"; $template = new HTML_Template_ITX("./templates"); $template->loadTemplatefile("example.9-11.tpl", true, true); $template->setVariable("MESSAGE",                        "Please fill in the details below to join"); $template->setVariable("SUBMITVALUE", "Join Now!"); $template->setVariable("FORMNAME", "custform"); $template->setVariable("SUBMITACTION", "   document.custform.firstname.hasNospaces = true;   document.custform.firstname.description = 'First Name';   document.custform.surname.description = 'Surname';   document.custform.address.description = 'Address';   document.custform.email.description = 'Email';   document.custform.email.isEmail = true;   document.custform.dob.isDate = true;   document.custform.dob.description = 'Date of Birth (99/99/9999)';   document.custform.salary.description = 'Salary';   document.custform.salary.isNumeric = true;   document.custform.salary.minNumber = 1;   document.custform.salary.maxNumber = 1000000;   document.custform.salary.hasNospaces = true;   return verify(document.custform);"); $template->setCurrentBlock("mandatoryinput"); $template->setVariable("MINPUTTEXT", "First name"); $template->setVariable("MINPUTNAME", "firstname"); $template->setVariable("MINPUTVALUE", ""); $template->setVariable("MINPUTSIZE", 50); $template->parseCurrentBlock("mandatoryinput"); $template->setCurrentBlock("mandatoryinput"); $template->setVariable("MINPUTTEXT", "Surname"); $template->setVariable("MINPUTNAME", "surname"); $template->setVariable("MINPUTVALUE", ""); $template->setVariable("MINPUTSIZE", 50); $template->parseCurrentBlock("mandatoryinput"); $template->setCurrentBlock("mandatoryinput"); $template->setVariable("MINPUTTEXT", "Address"); $template->setVariable("MINPUTNAME", "address"); $template->setVariable("MINPUTVALUE", ""); $template->setVariable("MINPUTSIZE", 50); $template->parseCurrentBlock("mandatoryinput"); $template->setCurrentBlock("mandatoryinput"); $template->setVariable("MINPUTTEXT", "Date of Birth (dd/mm/yyyy)"); $template->setVariable("MINPUTNAME", "dob"); $template->setVariable("MINPUTVALUE", ""); $template->setVariable("MINPUTSIZE", 50); $template->parseCurrentBlock("mandatoryinput"); $template->setCurrentBlock("mandatoryinput"); $template->setVariable("MINPUTTEXT", "Email"); $template->setVariable("MINPUTNAME", "email"); $template->setVariable("MINPUTVALUE", ""); $template->setVariable("MINPUTSIZE", 30); $template->parseCurrentBlock("mandatoryinput"); $template->setCurrentBlock("mandatoryinput"); $template->setVariable("MINPUTTEXT", "Annual salary (whole dollars)"); $template->setVariable("MINPUTNAME", "salary"); $template->setVariable("MINPUTVALUE", ""); $template->setVariable("MINPUTSIZE", 6); $template->parseCurrentBlock("mandatoryinput"); $template->parseCurrentBlock( ); $template->show( ); ?>

Example 9-14 is almost identical to the template in Example 8-8.

Example 9-14. The template used to produce the customer form
<!DOCTYPE HTML PUBLIC                  "-//W3C//DTD HTML 4.01 Transitional//EN"                  "http://www.w3.org/TR/html401/loose.dtd"> <html> <head>   <script type="text/javascript" src="/books/2/581/1/html/2/example.9-12.js">   </script>   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">   <title>Customer Details</title> </head> <body bgcolor="white"> <form name="{FORMNAME}" method="post" action="test.php"       onsubmit="{SUBMITACTION}"> <h1>Customer Details</h1> <h2>{MESSAGE}.     Fields shown in <font color="red">red</font> are mandatory.</h2> <table> <!-- BEGIN mandatoryinput --> <tr>   <td><font color="red">{MINPUTTEXT}:</font></td>   <td>   <input type="text" name="{MINPUTNAME}" value="{MINPUTVALUE}"          size={MINPUTSIZE}>   </td> </tr> <!-- END mandatoryinput --> <tr>    <td><input type="submit" value="{SUBMITVALUE}"></td> </tr> </table> </form> </body> </html>

The differences are that the JavaScript code from Example 9-12 is included using the src attribute (as discussed at the beginning of this case study), and that the FORMNAME and SUBMITACTION placeholders have been added to the form element. The FORMNAME is the name of the form, and the SUBMITACTION supports the onsubmit function call.



Web Database Application with PHP and MySQL
Web Database Applications with PHP & MySQL, 2nd Edition
ISBN: 0596005431
EAN: 2147483647
Year: 2003
Pages: 176

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net