Chapter 12. Working with JavaScript

You learned the key concepts behind the JavaScript language in Chapter 11; in this chapter, you see how these concepts come together in working scripts. By looking at several examples, you learn different ways in which JavaScript can interact with your web pages, some helpful coding practices for writing your own JavaScripts, and some shortcuts to creating interactive pages. The chapter is roughly split into two sections:

  • Creating your own basic scripts: The first section focuses on how to write your own basic scripts. Most of these examples work with form elements.

  • Using pre-written JavaScript libraries: The second section focuses on a number of scripts that have already been written and shows you how you can add powerful and complex features to your site with just a few lines of code.

By the end of the chapter, not only will you have learned a lot about using JavaScript in your pages, but you will also have seen many helpful tools and techniques you can use in your own pages.

Practical Tips for Writing Scripts

Before you start looking at the examples, I'd like to share a few practical hints on developing JavaScripts that should save you time.

Has Someone Already Written This Script?

There are thousands of free JavaScripts already on the Web, so before you start writing a script from scratch, it is worth searching to see if someone has already done all the hard work for you. Here are a couple of sites that will help you get going (and don't forget you can search using a search engine such as Google, too):

  • www.HotScripts.com

  • www.JavaScriptKit.com

  • www.webreference.com/programming/javascript/

  • http://JavaScript.Internet.com

Of course, for some tasks you will have to write your own script, but you may still find that someone has written a script that does something similar (and could learn something just by looking at how they approached the problem).

You will see more about this topic near the end of the chapter when you look at using existing JavaScript libraries.

Reusable Functions

Along with reusing other people's scripts and folders, you can also write code that you can reuse yourself. For example, you might build several sites that use a similar form that allows people to contact the site owners. On each contact form there might be several fields that are required, and you might decide to write a script to ensure that people fill in the required fields. Rather than writing a new script for each site, you can create a script that you can use on any contact form you write.

So, you should aim to make your code as reusable as possible, rather than writing a script that will only work with one page. You will see examples of this shortly.

Using External JavaScript Files

Whenever you are going to use a script in more than one page it's a good idea to place it in an external JavaScript file (a technique you learned about at the beginning of Chapter 11). For example, if you wanted to create a newsletter signup form on each page of your site, then you might use a script to check that the text entered into the e-mail address box is in a valid e-mail format. Rather than including this script on every page, if the script lives in an external JavaScript file:

  • You do not have to copy and paste the same code into several files.

  • The file size of the pages is smaller because the JavaScript is in one file that is included on each page rather than repeated in multiple pages.

  • If you need to change something about the script, you need to change only the one script, not every page that uses it.

Place Scripts in a Scripts Folder

When you use external scripts you should create a special scripts folder — just as you would an images folder. This helps improve the organization of your site and your directory structure. Whenever you need to look at or change a script, you know exactly where it will be.

You should also use intuitive names for your script files so that you can find them quickly and easily.

Form Validation

Form validation is one of the most common tasks performed using JavaScript. You have likely come across forms on the Web that have shown you a prompt when you have not entered a value into a field that requires one, or when you have entered the wrong kind of value; this is because the form has been validated. That is, a script has checked to see whether the text you have entered or choices you have made match some rules that the programmer has written into the page. For example, if you are expected to enter an e-mail address, these validation rules may check what you entered to ensure that it contains an @ symbol and at least one period or full stop. These kinds of rules help ensure that the data provided by users meets the requirements of the application before being submitted.

When to Validate

Validation can happen in two places: in the browser using JavaScript, and on the server using one of several languages such as ASP.NET or PHP. In fact, applications that collect important information using a form (such as e-commerce orders) are usually validated both in the browser and on the server. You may wonder why forms are validated in the browser if they will only get checked again when they reach the server; the reason is that it helps the user enter the correct data required for the job without the form being sent to the server, being processed, and then being sent back again if there are any errors. This has two key advantages:

  • It's quicker for the user because the form does not need to be sent to the server, processed, and returned to the user with any relevant error messages.

  • It saves the load on the server because some errors will get caught before the form is submitted.

It is very important to validate on the server because you cannot guarantee that the user has JavaScript enabled in his or her browser, and if a user entered a wrong value into a database or other program it could prevent the entire application from running properly. (It is also possible for hackers to bypass JavaScript if they are intending to send some incorrect information.)

What You Can Check For

When it comes to validating a form you cannot always check whether users have given you the correct information, but you can check whether they have given you some information in the correct format. For example, you cannot ensure that the user has entered his or her correct phone number; the user could be entering anyone's phone number, but you can check that it's a number rather than letters or other characters, and you can check that the number contains a minimum number of digits. As another example, you can't ensure someone has entered a real e-mail address rather than a false address, but you can check that whatever was entered followed the general structure of an e-mail address (including an @ sign and a period, and that it is at least seven characters long). So JavaScript form validation is a case of minimizing the possibility of user errors by validating form controls.

When it comes to form controls that allow users to indicate their choice from a selection of options (such as checkboxes, drop-down select boxes, and radio buttons), you can use JavaScript to check that a user has selected one of the options (for example, to check that a user has checked the terms and conditions).

How to Check a Form

There are several ways in which you can check a form. Usually when the user presses the submit button on a form, it triggers the onsubmit event handler on the <form> element, which in turn calls a validation function stored either in a separate script or in the head of the document. The function must then return true in order for the form to be sent, or, if an error is encountered, the function returns false and the user's form will not be sent — at which point the form should indicate to the user where there is a problem with the information the user entered.

If you use a validation function that is called by the onsubmit event handler, but the user's browser does not support JavaScript, then the form will still be submitted without the validation checks taking place.

In the validation functions you meet in this chapter, the first task will be to set a variable that can be returned to say whether the script found errors or not. At first, this is set to true (indicating that the form can be sent because problems were found); then as the script checks the values the user has entered, if the function finds an error this value can be turned to false to prevent the form from being submitted.

Some forms also check values as the user moves between form fields — in which case the values the user entered are passed to a function that checks that specific form control using the onblur event (which fires when that form control loses focus).

Checking Text Fields

You have probably seen forms on web sites that ask you to provide a username and password, and then to re-enter the password to make sure you did not mistype something. It might resemble Figure 12-1.

Figure 12-1

Figure 12.1. Figure 12-1

Let's take a look at the code for this form, and how it calls the JavaScript that will validate what the user has entered (ch12_eg01.html).

<form name="frmRegister" method="post" action="register.aspx"
  onsubmit="return validate(this);">
  <div>
    <label for="txtUserName">Username:</label>
<input type="text" name="txtUserName" id="txtUserName" size="12" />
  </div>
  <div>
    <label for="txtPassword">Password: </label>
    <input type="password" name="txtPassword" id="txtPassword" size="12" />
  </div>
  <div>
    <label for="txtPassword2">Confirm your password:</label>
    <input type="password" name="txtPassword2" id="txtPassword2" size="12"/>
  </div>
  <div>
    <input type="submit" value="Log in" />
  </div>
</form>

The opening <form> tag has an onsubmit attribute; when the user presses the submit button on the form, the script specified in this attribute will be run.

<form name="frmRegister" method="post" action="register.aspx"
  onsubmit="return validate(this);">

In this case, when the user presses the submit button, a function called validate() will run. Before the name of the function is the keyword return; this indicates that the validate() function will return a value of true or false (in order for the form to be submitted, the function must return true; if it returns false the form is not submitted).

The validate() function we are writing will take one parameter; it tells the function the form that you want to process. So, inside the parentheses of the validate() function, you can see the word this, which indicates that this is the form you wish to validate (as opposed to any other form that might appear on the page).

With a login form like this one, you might want to check a few things:

  • That the username is of a minimum length

  • That the password is of a minimum length

  • That the two passwords match

In this section, you are going to look at two different ways to approach the validate() function:

  • Creating a single function to check the form

  • Creating re-usable functions that are called by this function

In both cases, the validate() function will live in the <head> element, and will start by setting a variable called returnValue to true; if no errors are found this will be the value that the function returns, which will in turn allow the form to be sent. If an error is met, the variable will be set to false, and the form will not send.

Single Function Approach

The first approach we will look at to validate this form is to write a validation script especially for this one form, where all of the rules live inside the one function (ch12_eg01.html).

The function will be called validate() and it will expect to be told which form it is working with as a parameter of the function:

function validate(form) {

This is why the onsubmit attribute of the <form> element used the keyword this when calling the function, indicating that this is the form it wanted to process.

In the script, you want to collect the values that the user entered into the text controls and store these in variables. You identify the form controls within the page one by one, using the dot notation you met in the last chapter. You then collect the value the user typed into that control using the value property:

function validate(form) {
  var returnValue = true;
  var username = form.txtUserName.value;
  var password1 = form.txtPassword.value;
  var password2 = form.txtPassword2.value;

Because we have told the function which form we are working with, the dot notation can start with form rather than document.frmRegister; the following two lines would do exactly the same thing:

var username = document.frmRegister.txtUserName.value;
var username = form.txtUserName.value;

Now you can start to check whether the data that was entered by the user meets the criteria you require for the form to be submitted. First you can check whether the username is at least six characters long. The value the user entered into the username form control is already stored in a variable called username, and because it is a string (a set of letters or numbers), you can use the length property to tell how long it is.

If the username is not long enough, you will need to take action, so you place your validation test in an if statement. The following states that if the length of the variable is less than six characters long, the code in curly braces will be run:

if (username.length < 6) {
  username is less than 6 characters so do something
}

In this case, if it is less than six characters long, you will do three things:

  • Set the variable returnValue to false, so that the form will not be submitted.

  • Tell the user what has happened so that he or she can correct the error. For this example, we will use the JavaScript alert() function to create an alert box (like the ones you met in the last chapter) containing a message for the user.

  • Pass focus back to this item (using the JavaScript focus() method) on the form so that the user can change what he or she had put in this form.

Here you can see the if statement with all of these actions:

if(username.length < 6) {
  returnValue = false;
  alert("Your username must be at least
6 characters long.

  Please try again.");
  frmRegister.txtUserName.focus();
}

The alert box used to be a very popular way to show users errors on their forms (as it is a very simple technique of providing feedback). These days, it is more popular to write an error message into the page itself; however, this is more complicated, so you will see how to do that later in the chapter.

You may have noticed in the middle of the error message; this creates a line break in JavaScript.

Next you want to check the length of the first password. To do this, you can use the same approach. But if the password is not long enough you will empty both password controls, and give focus to the first password box:

if (password1.length < 6) {
  returnValue = false;
  alert("Your password must be at least
6 characters long.

    Please try again.");
  frmRegister.txtPassword.value = "";
  frmRegister.txtPassword2.value = "";
  frmRegister.txtPassword.focus();
}

If the code has gotten this far, the username and first password are both long enough. Now, you just have to check whether the value of the first password box is the same as the second one, as shown here. Remember that the != operator used in this condition means "not equal," so the if statement will take action if the value of password1 does not equal that of password2.

if (password1.value != password2.value) {
  returnValue = false;
  alter("Your password entries did not match.
Please try again.");
  frmRegister.txtPassword.value = "";
  frmRegister.txtPassword2.value = "";
  frmRegister.txtPassword.focus();
}

You can see here that when the user has entered passwords that do not match, the user is shown an alert box with an error message reporting that the password entries did not match. Also, the contents of both password inputs are cleared and the focus is passed back to the first password box.

Because a password input will show dots or asterisks rather than the characters, when a user makes a mistake with a password input, he or she will not be able to see where the mistake is. This is why I clear password boxes when a user has made a mistake in them.

The only thing left to do in this function is return the value of the returnValue variable — which will be true if all the conditions are met or false if not.

return returnValue;
}

Here is the function in its entirety (ch12_eg01.html):

function validate(form) {

  var returnValue = true;

  var username = form.txtUserName.value;
  var password1 = form.txtPassword.value;
  var password2 = form.txtPassword2.value;

if(username.length < 6) {
  returnValue = false;
  alert("Your username must be at least
6 characters long.

  Please try again.");
  document.frmRegister.txtUserName.focus();
}

if (password1.length < 6) {
  returnValue = false;
  alert("Your password must be at least
6 characters long.

  Please try again.");
  document.frmRegister.txtPassword.value = "";
  document.frmRegister.txtPassword2.value = "";
  document.frmRegister.txtPassword.focus();
}

if (password1 != password2) {
  returnValue = false;
  alert("Your password entries did not match.
Please try again.");
  document.frmRegister.txtPassword.value = "";
  document.frmRegister.txtPassword2.value = "";
  document.frmRegister.txtPassword.focus();
}
  return returnValue;
}

In Figure 12-2 you can see the result if the user's password is not long enough.

Figure 12-2

Figure 12.2. Figure 12-2

This example should have given you a good idea of how to check what a user has entered into a form against a set of rules.

Re-Usable Functions Approach

While the example you just saw works fine, you can save time and effort by writing code that you can re-use. For example, many programmers have a single JavaScript file that will contain a set of form validation functions they can use in any form that they write. These functions can check for things like the following:

  • Whether the user has entered text that is longer than the minimum required length as demonstrated with the username and password in the last example

  • Whether the text entered by the user consists only of numbers (no other characters) — which could be handy for telephone numbers or for asking customers the quantity of goods they want in an e-commerce store

  • Whether, when you are requesting an e-mail address, the user has entered that address in the correct form

JavaScript files that contain functions that can be re-used are often known as JavaScript libraries. Let's look at the same form again, but develop a validation approach that utilizes this approach.

Using the second approach, we still have to write a validate() function for each form, but the function is much shorter than the last one you saw. Its job is to pass values from the form to functions in the JavaScript validation library that does the real validation work. So, this time our validate() function will call two other functions in the JavaScript library:

  • validateConfirmPassword() will check that the two password fields match.

  • validateMinimumLength() will ensure that the user has entered a minimum number of characters for the form field.

You will look at these functions in a moment, but first let's look at the validate() function that calls them (which lives in the same page as the form); its job is to pass values from the form to the library functions. If there is an error, it will then prevent the submission of the form, and will tell the user the error message. Before you look at each line of the function individually, here is the entire function:

function validate(form) {
  var returnValue = "";
  returnValue += validateConfirmPassword(form.txtPassword,
      form.txtPassword2,
      'Your passwords did not match'),
  returnValue += validateMinimumLength(form.txtPassword, 6,
      'Your password must be at least 6 characters long'),
  returnValue += validateMinimumLength(form.txtUserName, 6,
      'Your username must be at least 6 characters long'),
  if (returnValue != "") {
    return false;
  }
  return true;
}

Let's take a closer look at this; it starts with the function name. The function takes one parameter, the form it has to validate, which is given in parentheses.

function validate(form) {

Then a variable called returnValue is declared; this time, rather than being set to true, it is set to an empty string using empty quotes.

var returnValue = "";

You then make three calls to functions, each of which checks a different aspect of the form. We'll look at each of these in turn in a moment. If these functions find a problem (because the user has not entered what you wanted), the functions will return an error. You may remember from the last chapter that += adds a value onto an existing variable, so if the function returns an error the error message is appended to the variable returnValue; if there is not an error, nothing will be added to the variable.

First you can see we're calling the validateConfirmPassword() function. We tell it the two password controls that we want it to check using the dot notation, along with the error message we want to display if the fields do not match.

returnValue += validateConfirmPassword(form.txtPassword, form.txtPassword2,
      'Your passwords did not match'),

Because the validateConfirmPassword() function will return the error message if the passwords do not match, we will know if there is a problem because the returnValue attribute will contain this error message; it will no longer be empty.

We then call the validateMinimumLength() function to check the length of the first password control. Here we are passing in the form control to check, the minimum number of characters the user can enter for that form control, and the error message to show if the form control is not that length.

returnValue += validateMinimumLength(form.txtPassword, 6,
    'Your password must be at least 6 characters long'),

We then check that the username entered is more than six characters. To do this we call the validateMinimumLength() function a second time. Here you can already start to see the benefits of code re-use; rather than repeating the entire function again we are using the same function we just used a second time, but this time we are telling it to check a different form control and display a different error message if there is a problem.

returnValue += validateMinimumLength(form.txtUserName, 6,
    'Your username must be at least 6 characters long'),

Having checked these three aspects of the form, if there is a problem returnValue will no longer be empty. The following if statement says if returnValue is not empty, the validate() function should return false to say that the form should not be submitted. Otherwise, if it is blank, we can return true, which will allow the form to be submitted.

if (returnValue != "") {
  return false;
}
return true;

You might notice that we start with the rules that apply to the last items in the form, and work backwards to the rules that apply to the first form controls. We do this because, when there is an error, we want to return focus to the form field with the error (just as we did in the first example). If there is more than one error, we want the user to start at the top of the form, and work through it correcting all errors, so we want the last error that the script processes to be the first one that the user will see.

As you will see in a moment, when these functions find an error, not only do they give focus to that form element, they also write the error message into the page, which means we need to change the form in the XHTML page slightly. Let's look at the form again; this time there are empty <span> elements after the form controls. These will hold any error messages that are returned. When designing your form, it is important to leave enough space in the page for these error messages. It is also very important that there be no spaces between the closing of the <input /> tag and the start of the <span> tag; otherwise, the form will not work.

<form name="frmRegister" method="post" action="register.aspx"
  onsubmit="return validate(this);">
  <div>
    <label for="txtUserName">Username:</label>
    <input type="text" id="txtUserName" size="12" /><span
       class="message"></span>
  </div>
  <div>
    <label for="txtPassword">Password: </label>
    <input type="password" id="txtPassword" size="12" /><span
       class="message"></span>
  </div>
  <div>
    <label for="txtPassword2">Confirm your password:</label>
    <input type="password" id="txtPassword2" size="12" /><span
       class="message"></span>
  </div>
  <div>
    <input type="submit" value="Log in" />
  </div>
</form>

So, now let's take a look at the function that will check that a user has entered a minimum number of characters into a specified form field.

function validateMinimumLength (control, length, errormessage) {
  var error="";
  document.getElementById(control.id).nextSibling.innerHTML="";
  if (control.value.length < length) {
    error = errormessage;
    document.getElementById(control.id).nextSibling.innerHTML=errormessage;
    document.getElementById(control.id).focus();
    }
  return error;
}

We are passing three things into the function so that it can perform its calculation:

  • The form control we want to check

  • The length of the text we expect the user to have entered

  • An error message we will show to the user if the control is not long enough

function validateMinimumLength (control, length, errormessage) {

You can see in the brackets the words control, length, and errormessage. Inside the function, we can use these words to refer to the values passed into the function when it was called. For example, look at this line:

if (control.value.length < length) {

We have an if statement checking whether the length of the control passed in (control) is less than the minimum number of characters we will allow (length).

If the user has not entered enough characters, we set the value of a variable called error (which was declared in the second line of the function) to contain the error message.

error = errormessage;

We then write the error message into the extra <span> element we added after each form control to display the problem.

document.getElementById(control.id).nextSibling.innerHTML=errormessage;

There is a lot going on in this one line, so let's break it down:

  • control.id gives you the value of the id attribute of the form control that you are working with.

  • document.getElementById(control.id) gives you the form control you are working with (the getElementById() method returns an element given an id attribute, and you have just seen how to get the value of the id attribute for the form control you are working with using control.id).

  • nextSibling returns the next element after this form control, which is the <span> element following it that will hold the error message.

  • innerHTML=errormessage adds the error message inside that <span> element.

If there is a problem, we also give the form control with the problem focus:

document.getElementById(control.id).focus();

We then return the error back to the validate() function in the page with the form (so that it can prevent the form from being submitted). Before looking at how we pass the error back, you may have noticed this line of code, which appears before the if statement that checks the length of the value entered into the form control:

document.getElementById(control.id).nextSibling.innerHTML="";

Its purpose is to ensure the <span> element next to the form control is blank. We need to do this because, once a form has been submitted and errors have been spotted, the user may resubmit the form again — in which case we need to clear any error messages, and then check the form control again, and only if there is still a problem display the error message again.

As you might imagine, if you are creating a lot of sites, with many forms, the ability to re-use validation functions saves you from having to code each form again and again.

Figure 12-3 shows the error message generated when the user has not entered a value for the username.

Figure 12-3

Figure 12.3. Figure 12-3

The replace() Method

Now that you have seen examples of how to validate text fields using a single function and using re-usable functions, it's time to look at some other ways that you can work with text inputs.

The JavaScript replace() method often comes in handy with text inputs because it allows you to replace certain characters with other characters. The simplest way to use the replace() method is to use the following syntax:

string.replace(oldSubString, newSubString);

For example, imagine you had a variable called message. The following line would look at this variable and replace instances of the letters "bad" with the letters "good":

message.replace('bad', 'good'),

Let's add this into a form so you can see it working; we have a <textarea> that contains the sentence "I think it would be a bad idea to make a badge." When the user clicks on the button, we will replace the letters bad with the word good. Rather than creating a function to demonstrate this, we can simply put the script we want to run in the onclick event of the button (ch12_eg03.html).

<form name="myForm">
  Message: <textarea name="myTextArea" id="myTextArea" cols="40" rows="10">
  I think it would be a bad idea to make a badge.</textarea>
  <input type="button" value="Replace characters bad"
         onclick="document.myForm.myTextArea.value =
         document.myForm.myTextArea.value.replace('bad', 'good')," />
</form>

If you look at the value of the onclick attribute, it takes whatever was in the text area and replaces any occurrence of the letters bad with the letters good. Figure 12-4 shows you what this might look like after the user has pressed the buttdon once.

Figure 12-4

Figure 12.4. Figure 12-4

There is something interesting to note here. The replace() function replaces the first instance of the string bad. If the button were pressed again, you would see another issue: the word "badge" would turn into "goodge". We can address both of these issues using something known as a Regular Expression inside the replace() function.

Regular Expressions

Regular Expressions provide a very powerful way to find a particular string, although the downside of this power is that they can become quite complicated. For example, look at this modified example of the replace() method we were just looking at (ch12_eg04.html):

replace(/bad/gi, 'good'),

There are four things going on in this Regular Expression; let's build up to this expression, starting with the letters bad that we want to replace:

  • /bad/ The forward slashes around the string bad indicate that it is looking for a match for that string.

  • /bad/g The g after the second slash (known as a flag) indicates that the document is looking for a global match across the whole of the string (without the g flag, only the first match in the string is replaced).

  • /bad/gi The i flag indicates that it should be a case-insensitive match (so the string bad should be replaced in any mix of characters in upper- and lowercase).

  • /bad/gi The  on either side of the string bad indicates a word boundary — each specifies that you just want to look for whole words — so the string will be replaced only if the string bad is a word on its own. The letters bad in the word badge would not be replaced because the regular expression says there should be a word boundary on both sides of the letters bad. (You cannot just check for the presence of a space on either side of the letters bad, because there might be punctuation next to one of the letters.)

Using Regular Expressions, you could also match more than one string using the pipestem character; the following example looks for a match with bad, terrible, or awful:

/bad|terrible|awful/

Note that if you want to search for any of the following characters, they must be escaped because they have special meanings in Regular Expressions:

 | ( ) [ { ^ $ * + ? .

If you want to escape these characters, they must be preceded by a backslash (for example / / matches a backslash and /$/ matches a dollar sign).

The table that follows lists some other interesting characters used in regular expressions.

Expression

Meaning

Linefeed

Carriage return

Tab

v

Vertical tab

f

Form-feed

d

A digit (same as [0-9], which means any digit 0 through 9)

D

A non-digit (same as [^0-9] where ^ means not)

w

A word (alphanumeric) character (same as [a-zA-Z_0-9])

W

A non-word character (same as [^a-zA-Z_0-9])

s

A white-space character (same as [ v f])

S

A non-white-space character (same as [^ v f])

For a slightly more complex example, if you wanted to replace all carriage returns or linefeeds with the <br /> tag, you could use the following (ch12_eg05.html):

replace(/
|
|
|

|/g),'<br />'),"

In this case, the replace() method is looking for either linefeeds using or carriage returns using . Then these are being replaced with <br />. Figure 12-5 shows you what this example could look like with the carriage returns and line feeds replaced with <br /> tags. If you needed to replace carriage returns or line feeds with the <br /> element, it is more likely that task would be done behind the scenes when the form is submitted, rather that giving the user a button to replace these characters. But it does illustrate how to use the replace() function with a Regular Expression.

Figure 12-5

Figure 12.5. Figure 12-5

Testing Characters Using test() and Regular Expressions

Regular Expressions really come into their own when you need to test whether strings entered by users conform to a pattern. For example, Regular Expressions can be used to test whether the string follows a pattern for e-mail addresses, for an amount of currency, or for a phone number.

To check whether a value a user has entered matches a regular expression, you can use the test() method, which takes two parameters: the Regular Expression and the value the user entered. The test() method returns true if the value entered by the user matches the regular expression, and false if it does not.

Here is an example of a function that checks whether a user entered a currency (ch12_eg06.html). We will look at it line by line in a moment:

function validate(form) {
  var returnValue = true;
  var amountEntered = form.txtAmount.value;

  if (!/^d+(.d{1,2})?$/.test(amountEntered))
  {
    alert("You did not enter an amount of money");
    document.frmCurrency.txtAmount.focus();
    returnValue = false;
  }

return returnValue;
}

To start, a variable called returnValue is set to true; this is what will be returned from the function unless we find an error. Then a variable is set to hold the value the user entered into the form:

var returnValue = true;
var amountEntered = form.txtAmount.value;

Next, we have to test whether the value the user entered is a currency. We will use the test() method with the following regular expression:

/^d+(.d{1,2})?$/

The test() method is used inside an if statement; note that the first exclamation mark is there to say "If the test fails, perform an action":

if (!/^d+(.d{1,2})?$/.test(amountEntered)){

If the test fails, we have to tell the user, give the focus back to that form control, and set the variable returnValue to have a value of false.

alert("You did not enter an amount of money");
document.frmCurrency.txtAmount.focus();
returnValue = false;

Here is the simple form to test this example:

<form name="myForm" onsubmit="return validate(this);"
      action="money.aspx" method="get">
  Enter an amount of money here $
  <input type="text" name="txtAmount" id="txtAmount" size="7" />
  <input type="submit" value="Check format" />
</form>

Figure 12-6 shows this form in action.

Figure 12-6

Figure 12.6. Figure 12-6

Regular Expressions are not the easiest thing to learn to write, and there are entire books devoted to writing complex expressions. However, the table that follows lists some helpful ones that you can use to get you started.

Test for

Description

Regular Expression

White space

No white-space characters.

S/;

Alphabetic characters

No characters of the alphabet or the hyphen, period, or comma may appear in the string.

/[^a-z -.']/gi;

Alphanumeric characters

No letters or number may appear in the string.

/[^a-z0-9]/gi;

Credit card details

A 16-digit credit card number following the pattern XXXX XXXX XXXX XXXX.

/^d{4}([-]?d{4}){3}$/;

Decimal number

A number with a decimal place.

/^d+(.d+)?$/;

Currency

A group of one or more digits followed by an optional group consisting of a decimal point plus one or two digits.

/^d+(.d{1,2})?$/;

E-mail address

An e-mail address.

/^w(.?[w-])*@w(.? [w-])*.[a-z]{2,6}(. [a-z]{2})?$/i;

Select Box Options

When you want to work with a drop-down select box, the select object (which represents the select box) has a very helpful property called selectedIndex, which tells you which option the user has selected.

Because this is an index, it will start at 0, so if the user has selected the first option, the selectedIndex property will have a value of 0. If the user selects the second option, the selectedIndex property will be given a value of 1, the third will be given a value of 2, and so on.

By default, if the user does not change the value that the control has when the page loads, the value will be 0 for a standard select box (because the first option is automatically selected when the form loads). In a multiple select box (which allows users to select more than one option from the list), the default value will be 1 if none of the options are selected (which indicates that the user has not selected any option).

Look at the following simple select box; the first option in this select box asks the user to select a suit of cards (ch12_eg07.html):

<form name="frmCards" action="cards.aspx" method="get"
      onsubmit="return validate(this)">
  <select name="selCards" id="selCards">
    <option>Select a suit of cards</option>
    <option value="hearts">Hearts</option>
    <option value="diamonds">Diamonds</option>
    <option value="spades">Spades</option>
    <option value="clubs">Clubs</option>
  </select>
  <input type="submit" value="Send selection" />
</form>

Now, to check that one of the suits of cards has been selected, you have the validate() function, which will have been passed the form object as a parameter. In the case of this example, if the selectedIndex property of the object representing the select box has a value of 0, then you have to alert the users that an option has not been selected and ask them to do so.

function validate(form) {
  var returnValue = true;
  var selectedOption = form.selCards.selectedIndex;
  if (selectedOption==0)
  {
    returnValue = false
    alert("Please select a suit of cards.");
  }
  return returnValue;
}

In Figure 12-7, you can see the warning if the user has not selected a suit of cards.

Figure 12-7

Figure 12.7. Figure 12-7

If you wanted to access the value attribute on the selected option (rather than its index number) you would use the following syntax:

form.selCards.options[selected].value

This is because you need to look at which of the [option] elements was selected to get its value rather than just the index number of the selected element.

Radio Buttons

A group of radio buttons is different from other form controls in that only one option from a group can be selected at a time, and all members of the group share a value for the name attribute. Scripts that interact with radio buttons usually want to either check that one of the options has been selected, or find out which of the options has been selected.

A set of radio buttons is represented as an array in JavaScript, and in order to find out which one was selected, you need to loop through the array, looking at the checked property of each radio button. If it is selected, the value will be true and false if not. For example, the following is a form with four radio buttons (ch12_eg08.html):

<form name="frmCards" action="cards.aspx" method="post"
      onsubmit="return validateForm(this)" >
  <p>Please select a suit of cards.</p>
  <p><input type="radio" name="radSuit" value="hearts" /> Hearts</p>
  <p><input type="radio" name="radSuit" value="diamonds" /> Diamonds</p>
  <p><input type="radio" name="radSuit" value="spades" /> Spades</p>
  <p><input type="radio" name="radSuit" value="clubs" /> Clubs</p>
  <p><input type="submit" value="Submit choice" /></p>
</form>

In order to loop through each of the radio buttons in the collection and see which one has a checked property, you will use a for loop.

The following function uses a variable I will call radioChosen to indicate whether one of the radio buttons has been chosen. Its value starts off as false, and if a button has been chosen the value is set to true. Once the loop has gone through each of the radio buttons, an if statement tests whether one of the options has been selected by looking at the value of this variable:

function validate(form) {
   var radioButtons = form.radSuit;
   var radioChosen = false;
   for (var i=0; i<radioButtons.length; i++) {
    if (radioButtons[i].checked)
     {
       radioChosen=true;
       returnValue=true;
     }
   }
   if (radioChosen == false) {
     returnValue = false;
     alert("You did not select a suit of cards");
   }
return returnValue;
}

While the order of attributes on an element should not matter in XHTML, there was a bug in Netscape 6 and some versions of Mozilla, which means it will show a checked property of the radio button only if the type attribute is the first attribute given on the <input /> element.

You can see the result in Figure 12-8.

Figure 12-8

Figure 12.8. Figure 12-8

Another way to ensure that one of the options is selected is to preselect an option when the page loads.

Checkboxes

Checkboxes allow a user to select zero, one, or more items from a set of choices (they are not mutually exclusive as radio buttons are). As with radio buttons, when a group of checkboxes share the same name they are made available in JavaScript as an array.

The following is a slight change to the last example using checkboxes instead of radio buttons, and the user can select more than one suit of cards (ch12_eg09.html):

<form name="frmCards" action="cards.aspx" method="post">
  <p>Please select one or more suits of cards.</p>
  <p><input type="checkbox" name="chkSuit" value="hearts" /> Hearts</p>
  <p><input type="checkbox" name="chkSuit" value="diamonds" /> Diamonds</p>
  <p><input type="checkbox" name="chkSuit" value="spades" /> Spades</p>
  <p><input type="checkbox" name="chkSuit" value="clubs" /> Clubs</p>
  <p><input type="button" value="Count checkboxes"
      onclick="countCheckboxes(frmCards.chkSuit)" /></p>
</form>

The following is the function that counts how many checkboxes have been selected and displays that number to the user. As with the last example, if no checkboxes have been selected, you can alert the user that she must select an option value.

function countCheckboxes(field) {
  var intCount = 0
  for (var i = 0; i < field.length; i++) {
     if (field[i].checked)
         intCount++; }
  alert("You selected " + intCount + " checkbox(es)");
}

You can see the form in Figure 12-9 where the user has selected two checkboxes.

Figure 12-9

Figure 12.9. Figure 12-9

Preventing a Form Submission Until a Checkbox Has Been Selected

If you want to ensure that a single checkbox has been selected — for example, if you want a user to agree to certain terms and conditions — you can do so by adding a function to the onsubmit event handler similar to those you have seen already. The function checks whether the checkbox has been checked, and if the function returns true the form will be submitted. If the function returns false, the user would be prompted to check the box. The function might look like this (ch12_eg10.html):

function checkCheckBox(myForm){
  if (myForm.agree.checked == false)
  {
    alert('You must agree to terms and conditions to continue'),
    return false;
  } else
    return true;
}

Another technique you may sometimes see is to use script to simply disable the Submit button until users have clicked the box to say that they agree with the terms and conditions.

If you use a script to disable a form control until a user has clicked on an option, you should disable the control in the script when the page loads rather than using the disabled attribute on the element itself. This is important for those who do not have JavaScript enabled in their browsers. If you use the disabled attribute on a <form> element and users do not have JavaScript enabled, they will never be able to use that form control. However, if you have used a script to disable it when the page loads, then you know that the script will be able to re-enable the form control when the user clicks the appropriate box. This is a great reminder that JavaScript should be used to enhance usability of pages and should not be required in order to use a page.

The following is a very simple page with a form. When the page loads, the Submit button is disabled in the onload event. If the user clicks the chkAgree checkbox, then the Submit button will be re-enabled (ch12_eg10.html):

<body onload="document.frmAgree.btnSubmit.disabled=true">
<form name="frmAgree" action="test.aspx" method="post">
I understand that this software has no liability:
<input type="checkbox" value="0" name="chkAgree" id="chkAgree"
       onclick="document.frmAgree.btnSubmit.disabled=false" />
<input type="submit" name="btnSubmit" value="Go to download" /><br />
<p>You will not be able to submit this form unless you agree to the
   <a href="terms.html">terms and conditions</a> and check the terms and
   conditions box.</p>
</form>
</body>

You can see this example in Figure 12-10. Note how there is an explanation of why the Submit button might be disabled. This helps users understand why they might not be able to click the Submit button.

This technique can also be used with other form controls — you will see an example that enables a text input later in the chapter.

Figure 12-10

Figure 12.10. Figure 12-10

Form Enhancements

The examples you are going to meet in this section do not actually help you validate a form; rather, they simply enhance the usability of a form.

Focus on First Form Item

If a lot of users are likely to interact with your page using a form, you can give focus to that text box when the page loads so that users do not have to move their mouse, click the text input, and then move their hands back to the keyboard before they enter any text. You might choose to do this on a page where there is a prominent login box or an option to search your site.

To give focus to the first text input on a form, simply add an onload event handler to the <body> element of the document. This handler selects the form control that you want to highlight and uses the focus() method of that control to give it focus, as follows (ch12_eg11.html):

<body onload="document.myForm.myTextBox.focus();">

When the page loads, the cursor should be flashing in the form control that you have selected, ready for the user to enter some text as shown in Figure 12-11 (some browsers may also have other ways of indicating an active text box, such as a highlighted border or shaded background).

Note that the onload event fires when the complete page has loaded (not as soon as the browser loads that individual element). This is worth bearing in mind because, if you have a very complicated page that takes a long time to load, this might not be a great option for the user; the focus might not be passed to the form until after the user has already had a chance to start typing in that field.

Figure 12-11

Figure 12.11. Figure 12-11

Auto-Tabbing Between Fields

The focus() method can also be used to pass the focus of one control to another control. For example, if one of the controls on a form is to provide a date of birth in MM/DD/YYYY format, then you can move focus between the three boxes as soon as the user enters a month, and then again once the user has entered a day (ch12_eg12.html):

<form name="frmDOB">
  Enter your date of birth:<br />
  <input name="txtMonth" id="txtMonth" size="3" maxlength="2"
       onkeyup="if(this.value.length>=2)
       this.form.txtDay.focus();"/>
  <input name="txtDay" id="txtDay" size="3" maxlength="2"
       onkeyup="if(this.value.length>=2)
       this.form.txtYear.focus();" />
  <input name="txtYear" id="txtYear" size="5" maxlength="4"
       onkeyup="if(this.value.length>=4)
       this.form.submit.focus();" />
  <input type="submit" name="submit" value="Send" />
</form>

This example uses the onkeyup event handler to check that the length of the text the user has entered is equal to or greater than the required number of characters for that field. If the user has entered the required number of characters, the focus is moved to the next box.

Note how the length of the text input is discovered using this.value.length. The this keyword indicates the current form control, whereas the value property indicates the value entered for the control. Then the length property returns the length of the value entered for the control. This is a quicker way of determining the length of the value in the current form control than the full path, which would be as follows:

document.fromDOB.txtMonth.value.length

The other advantage of using the this keyword rather than the full path is that the code would work if you copied and pasted these controls into a different form, as you have not hard-coded the name of the form.

You can see this example in Figure 12-12; the user has entered an appropriate number of digits in one field so the focus is moved on to the next.

Figure 12-12

Figure 12.12. Figure 12-12

You might have noticed that the value of the size attribute is also one digit larger than the maximum length of the field to ensure that there is enough space for all of the characters (otherwise the width of the control can sometimes be slightly too small to see all of the characters at once).

If you use this technique, it is always worth testing the form on a few users to check if it is acting as an enhancement. This is important because some web users have become used to pressing the tab key very quickly after entering short numbers (such as individual components of dates of birth) to take them to the next form control.

I have also seen this technique used to allow users to enter their credit card details using four blocks of four codes. While 16 digits is the most common length for a credit card number, and they are often printed in blocks of 4 digits, some Visa cards, for example, contain 13 digits and some American Express cards use 15 digits. So this is not a good idea.

Disabling a Text Input

Sometimes you will want to disable a text input until a certain condition has been met — just as the Submit button was disabled until the user clicked the checkbox to agree to terms and conditions in Figure 12-10.

This example features a form that asks users how they heard about the site; radio buttons are used for several options such as Friend, TV ad, magazine ad, and Other. If the user selects the Other option, the text input next to that option allows the user to indicate how they heard about the site. You can see the form in Figure 12-13.

In this example, it's not just a case of enabling the text box when the user selects the other radio button; you really need to check the value of each radio button as it is selected — after all, if the user selects Other as his or her first choice, but then changes her mind and selects TV or one of the other options, you will want to disable the text input and change its value again. Therefore, each time the user selects a radio button, a function in the head of the document is called that is responsible for enabling and disabling the control and setting values.

Figure 12-13

Figure 12.13. Figure 12-13

First, here is the form that gives users the options (ch12_eg13.html). Note how the text input is disabled using the onload event handler of the <body> element and that the text input does not use the disabled attribute (this is the same as the earlier example with the Submit button).

<body onload="document.frmReferrer.txtOther.disabled=true;
              document.frmReferrer.txtOther.value='not applicable' ">
<h2>How did you hear about us?</h2>
<form name="frmReferrer">
  <input type="radio" name="radHear" value="1"
         onclick="handleOther(this.value);" />From a friend<br />
  <input type="radio" name="radHear" value="2"
         onclick="handleOther(this.value);" />TV Ad<br />
  <input type="radio" name="radHear" value="3"
         onclick="handleOther(this.value);" />Magazine Ad<br />
  <input type="radio" name="radHear" value="4"
         onclick="handleOther(this.value);" />Newspaper Ad<br />
  <input type="radio" name="radHear" value="5"
         onclick="handleOther(this.value);" />Internet<br />
  <input type="radio" name="radHear" value="other"
         onclick="handleOther(this.value);" />Other... Please specify:
  <input type="text" name="txtOther" />
</form>

As you can see from this form, every time the user selects one of the options on this form, the onclick event calls a function called handleOther(). This function is passed the value of the form control as a parameter.

Looking at the function, you can see a simple if...else statement that checks whether the value of the selected form control is equal to the text other (remember that checking whether one value is equal to another value uses two equal signs because the single equal sign is used to set a variable). If the value of the selected radio button is "other," the textbox's disabled property is set to false, and the value cleared. For all other options the textbox is disabled and its value set to not applicable.

function handleOther(strRadio) {
  if (strRadio == "other") {
    document.frmReferrer.txtOther.disabled = false;
    document.frmReferrer.txtOther.value = '';
  }
  else {
    document.frmReferrer.txtOther.disabled = true;
    document.frmReferrer.txtOther.value = 'not applicable';
  }
}

Case Conversion

There are times when it is helpful to change the case of text a user has entered to make it all uppercase or all lowercase — in particular because JavaScript is case-sensitive. To change the case of text, there are two built-in methods of JavaScript's string object:

  • toLowerCase()

  • toUpperCase()

To demonstrate, here is an example of a text input that changes case as focus moves away from the text input (ch12_eg14.html):

<form>
  <input type="text" name="case" size="20"
         onblur="this.value=this.value.toLowerCase();" />
</form>

If your form data is being sent to a server, it is generally considered better practice to make these changes on the server because they are less distracting for users — a form that changes letter case as you use it can appear a little odd to users.

Trimming Spaces from Beginning and End of Fields

You might want to remove spaces (white space) from the beginning or end of a form field for many reasons, even simply because the user did not intend to enter it there. The technique I will demonstrate here uses the substring() method of the String object, whose syntax is:

substring(startPosition, endPosition)

This method returns the part of the string specified by the start and end points — if no end position is given, then the default is the end of the string. The start and end positions are zero-based, so the first character is 0. For example, if you have a string that says Welcome, then the method substring(0, 1) returns the letter W.

First we will look at removing white space from the start of a string. To do this the substring() method will be called upon twice. First you use the substring() method to retrieve the first letter that the user has entered into a text control. If this character is a space, you can then call the substring() method a second time to remove the space.

So the first call to the substring() goes in a while loop like so:

while (this.value.substring(0,1) == ' ')

The second time the substring() method is called, it selects the value of the control from the second character to the end of the string (ignoring the first character). This is set to be the new value for the form control; so you have removed the first character, which was a space:

this.value = this.value.substring(1, this.value.length);

This whole process of checking whether the first character is a blank, and then removing it if it is, will be called using the onblur event handler; so when focus moves away from the form control, the process starts. Here you can see the entire process using the while loop to indicate that, for as long as the first character is a blank, it should be removed using the second call to the substring() method (ch12_eg15.html).

<form>
  <input type="text" name="txtName" size="100"
    value=" Enter text leaving whitespace at start. Then change focus."
    onblur="while (this.value.substring(0,1) == ' ')
    this.value = this.value.substring(1, this.value.length);" /><br />
</form>

To trim any trailing spaces, the process is similar but reversed. The first substring() method collects the last character of the string, and if it is blank removes it, as follows:

<form>
<input type="text" name="txtName" size="100"
       value="Enter text leaving whitespace at end. Then change focus. "
       onblur="while (this.value.substring
          (this.value.length-1,this.value.length) == ' ')
          this.value = this.value.substring(0, this.value.length-1);" /><br/>
</form>

As long as you are not targeting browsers as old as Netscape 4 and IE4, you can alternatively use a Regular Expression to trim the spaces, as follows:

<form>
  <input type="text" name="removeLeadingAndTrailingSpace" size="100"
    value=" Enter text with white space, then change focus. "
    onblur="this.value=this.value.replace(/^ s+/, ").replace(/s+$/,");"
  /><br />
</form>

This removes both trailing and leading spaces.

Regular Expressions are quite a large topic in themselves and were introduced earlier in this chapter. If you want to learn more about them, refer to Beginning JavaScript, 2nd Edition by Paul Wilton (Wrox, 2000).

Selecting All the Content of a Text Area

If you want to allow users to select the entire contents of a text area (so they don't have to manually select all the text with the mouse), you can use the focus() and select() methods.

In this example, the selectAll() function takes one parameter — the form control that you want to select the content of(ch12_eg16.html):

<html>
<head><title>Select whole text area</title>
<script language="JavaScript">
  function selectAll(strControl) {
    strControl.focus();
    strControl.select();
  }
</script>
</head>
<body>
  <form name="myForm">
    <textarea name="myTextArea" rows="5" cols="20">This is some
text</textarea>
    <input type="button" name="btnSelectAll" value="Select all"
           onclick="selectAll(document.myForm.myTextArea);" />
  </form>
</body>
</head>
</html>

The button that allows the user to select all has an onclick event handler to call the selectAll() function and tell it which control it is whose contents should be selected.

The selectAll() function first gives that form control focus using the focus() method and then selects its content using the select() method, because the form control must gain focus before it can have its content selected. The same method would also work on a single-line text input and a password field.

Check and Uncheck All Checkboxes

If there are several checkboxes in a group of checkboxes, it can be helpful to allow users to select or clear a whole group of checkboxes at once. The following are two functions that allow precisely this:

function check(field) {
  for (var i = 0; i < field.length; i++) {
    field[i].checked = true;}
}
function uncheck(field) {
  for (var i = 0; i < field.length; i++) {
    field[i].checked = false; }
}

In order for these functions to work, more than one checkbox must be in the group. You then add two buttons that call the check or uncheck functions, passing in the array of checkbox elements that share the same, name such as the following (ch12_eg17.html):

<form name="frmSnacks" action="">
   Your basket order<br />
   <input type="checkbox" name="basketItem" value="1" />Chocolate
   cookies<br />
   <input type="checkbox" name="basketItem" value="2" />Potato chips<br />
   <input type="checkbox" name="basketItem" value="3" />Cola<br />
   <input type="checkbox" name="basketItem" value="4" />Cheese<br />
   <input type="checkbox" name="basketItem" value="5" />Candy bar<br /><br
/>
   <input type="button" value="Select All"
          onclick="check(document.frmSnacks.basketItem);" />

   <input type="button" value="Deselect All"
          onclick="uncheck(document.frmSnacks.basketItem);" />
</form>

You can see how this form appears in Figure 12-14.

Figure 12-14

Figure 12.14. Figure 12-14

This could also be combined into a single function, which could be called from the same button, such as the following:

function checkUncheckAll(field) {
 var theForm = field.form, i = 0;
 for(i=0; i<theForm.length;i++){
   if(theForm[i].type == 'checkbox' && theForm[i].name != 'checkall'){
   theForm[i].checked = field.checked;
   }
  }
}

JavaScript Libraries

The examples you have seen so far in this chapter have been designed to give you a better understanding of how JavaScript can be integrated into your XHTML documents — how events (such as a user hovering over, or clicking on, an element) can be used to trigger JavaScript functions.

In the case of the form validation script, you have seen an example of how to include an external JavaScript into a page with one line of code, and how the functionality of that script can be used in multiple web pages. This demonstrated that if you write a script carefully, once the work has been done, you can use it again and again in other pages with minimum effort.

JavaScript files that contain functions that you want to use in several pages are often referred to as JavaScript libraries(because, once you have included the file in your page, you can borrow itsfunctionality).

In this section, you are going to meet several scripts that offer functionality you might want to use in several of your web pages. For example, you will see how to create:

  • Animated effects and drag-and-drop lists

  • Lightboxes and modal windows

  • Sortable tables

  • Calendar controls

  • Auto-completing text inputs

Each of these example scripts is built on top of a one of the major JavaScript libraries that you will see used again and again if you explore other examples on the Web. So, the examples you meet in this chapter are built upon:

  • Scriptaculous (which is actually built on top of another JavaScript library called Prototype)

  • JQuery

  • MochiKit

  • Yahoo User Interface (YUI)

While you will meet several helpful scripts before the end of this chapter, the real purpose of this section is to illustrate how you can easily integrate this type of JavaScript code (that someone else has written) into your pages to offer complex functionality with minimum effort.

I have included versions of each of these libraries with the code download for this chapter. If you look in the code folder for Chapter 12, you will see inside the scripts folder that there are folders called scriptaculous, mochikit, and yui (each folder corresponding to the three libraries you will be using). It is worth, however, checking the web sites for each of these libraries as they may have been updated since this book was written.

Animated Effects Using Scriptaculous

To start with, I am going to show you some basic animation effects that can be created using a JavaScript library called Scriptaculous, which is built on top of another JavaScript library called Prototype. I have included a copy of Scriptaculous 1.8.2 and Prototype 1.6.0 with the code download for this chapter; however, you can check for more recent versions and download your own copy of these files from http://script.aculo.us/.

Scriptaculous can help you with many kinds of tasks: animation, adding drag-and-drop functionality, building editing tools, and creating autocompleting text inputs. It also includes utilities to help create DOM fragments. But in this section, you will just focus on some of the animation effects. (You will see examples of drag-and-drop and autocompleting text inputs later in the chapter.)

Scriptaculous contains many functions that help you create different types of animations. The first example is going to feature four of these animated effects, and it will demonstrate how easily you can add powerful features into your page with very little code. You can see what this page will look like in Figure 12-16, although you really need to try the example to see the actual animation by opening ch12_eg18.html in your browser.

Figure 12-16

Figure 12.16. Figure 12-16

As you may remember from the last chapter, JavaScript programmers use objects to represent real life objects or concepts. In the case of the Scriptaculous library, the animated effects are handled by an object called Effect. There are different types of effects, which are each called using different methods. For example, to create a shaking effect you need to call the Shake() method of the Effect object. To create a fading effect you use the Fade() method of the Effect object. Some of these methods can take different parameters that allow you to vary how the effect works.

In order to use the animated effects and other features of the Scriptaculous library, you need to add both the prototype.js library and the scriptaculous.js library, which I have placed in a folder called scriptaculous:

<script src="scripts/scriptaculous/prototype.js"
  type="text/javascript"></script>
<script src="scripts/scriptaculous/scriptaculous.js"
   type="text/javascript"></script>

In the body of the page, there is a box to demonstrate each effect; when you click on the box, the effect is triggered. Each box is created using a <div> element, inside which I have written the name of the Effect object and the method that box will demonstrate. Let's start by looking at the first box, which demonstrates the shake effect:

<div id="effect-shake" onclick="Effect.Shake(this)">
  Effect.Shake()
</div>

As you already know, the event attributes allow events to trigger JavaScripts. In this example, the onclick event causes the Effect object's Shake() method to be called. As with the forms you looked at earlier, the keyword this (which you can see inside the parentheses of the Shake() method) indicates that we want to create a shake animation for this element.

If you wanted the effect to be triggered by the user interacting with a different element, you could pass the Shake() method the id of the box you want to shake. For example, here is a simple link; when the user clicks on the words "shake the above box," the first box will shake.

<a onclick="Effect.Shake('effect-shake')">
  shake the above box
</a>

Let's look at the second example, which shrinks the box when you click on it until it vanishes. It is very similar but this time we are calling the Shrink() method of the Effect object.

<div id="demo-effect-shrink" onclick="Effect.Shrink(this);">
    Effect.Shrink
</div>

Because the box disappears once it has shrunk, you can call the Appear() method of the Effect object to make it re-appear. You cannot call this method right away; otherwise, you will not be able to see the effect working, so you can call this method after a set period using the setTimeout() method of the window object (this is a JavaScript object, not one created by one of the JavaScript libraries). In this case, the Appear() method is called after 2500 milliseconds.

<div id="demo-effect-shrink" onclick="Effect.Shrink(this);
window.setTimeout('Effect.Appear('effect-shrink')',2500);">

Here are the other two effects:

<div id="effect-fade" onclick="Effect.Fade(this);
    window.setTimeout('Effect.Appear('effect-fade')',2500);">
      Effect.Fade()
</div>

<div id="effect-puff" onclick="Effect.Puff(this);
    window.setTimeout('Effect.Appear('effect-puff')',2500);">
      Effect.Puff()
</div>

You do not need to know how the Script creates these effects; all you need to know is the syntax of the method that calls each effect.

As you can see, this is a very simple way of creating animated effects using JavaScript; one that lots of other scripts make use of.

Drag-and-Drop Sortable Lists Using Scriptaculous

Having seen how simple it is to create some animations using Scriptaculous, let's have a look at an example of something you might want to do in a user interface: create drag-and-drop lists. You may have seen some sites where you can re-order lists (such as "to do" lists or top 10 lists) just by dragging and dropping the elements.

You can see the example you are going to build in Figure 12-17; when the page loaded, the boxes were in numerical order. However, they have now been dragged and dropped to a different order.

Figure 12-17

Figure 12.17. Figure 12-17

In this example (ch12_eg19.html), you need to include the Scriptaculous and Prototype libraries again. Then you have a simple unordered list (there are some CSS rules in the head of the document that control the presentation of the list to make each list item appear in its own box).

<script src="scripts/prototype.js" type="text/javascript"></script>
<script src="scripts/scriptaculous.js" type="text/javascript"></script>
<style type="text/css">
  li {border;1px solid #000000; padding:10px; margin-top:10px;
  font-family:arial, verdana, sans-serif;background-color:#d6d6d6;
  list-style-type:none; width:150px;}
</style>
</head>
<body>
<ul id="items_list">
   <li id="item_1">Item 1</li>
   <li id="item_2">Item 2</li>
   <li id="item_3">Item 3</li>
   <li id="item_4">Item 4</li>
</ul>

In order to make this list sortable, you just need to add one <script> element after the list:

<script type="text/javascript" language="javascript">
   Sortable.create("items_list",{dropOnEmpty:true,constraint:false});
</script>

Here you are using the Sortable object that is part of the Scriptaculous library, and calling its create() method to turn the list into a sortable one. The create() method takes two parameters:

  • The first is the value for the id attribute of the unordered list element, in this case items_list.

  • The second features options that describe how the sortable list should work. The first of the options specified here is dropOnEmpty with a value of true to indicate that element should only be dropped between elements, not on top of another one, and the constraint property, which is set to false. (If this were left off or true it would allow items to be moved along a vertical axis only, whereas set to false you can move the list items to the left or right while dragging them).

This kind of drag-and-drop list is often linked to some kind of functionality that will update a database (such as allowing users to re-order a "to do" or preference list), which would involve a script on the server written in a language such as ASP.NET or PHP code. However, this example does demonstrate something that is achieved very easily with just a few lines of code, thanks to the Scriptaculous library.

Creating a Lightbox

A lightbox is the term given to an image (or set of images) that opens up in the foreground of the browser without reloading the page. The image appears in the middle of the browser window, and the rest of the page is often dulled out. Figure 12-18 shows an example of a lightbox activated on a page (ch12_eg20.html).

Figure 12-18

Figure 12.18. Figure 12-18

The script we will be looking at in this section is called Lightbox2, and was written by Lokesh Dhakar. It is based on the Prototype and Scriptaculous libraries so we will include them as well as the lightbox script.

So, to create a lightbox, first you add in the following three scripts (ch12_eg20.html):

<script type="text/javascript" src="scripts/scriptaculous/prototype.js">
</script>
<script type="text/javascript"
    src="scripts/scriptaculous/scriptaculous.js?load=effects,builder"></
script>
<script type="text/javascript" src="scripts/lightbox.js"></script>

There are also some CSS styles that are used in the lightbox, so you can either link to this style sheet or include the rules from it in your own style sheet:

<link rel="stylesheet" href="css/lightbox.css" type="text/css"
    media="screen" />

Finally, you create a link to each of the images in the lightbox; here you can see a lightbox that contains three images:

<a href="images/image-1.jpg" rel="lightbox[Japan]">Picture 1</a>
<a href="images/image-2.jpg" rel="lightbox[Japan]">Picture 2</a>
<a href="images/image-3.jpg" rel="lightbox[Japan]">Picture 3</a>

Note the use of the rel attribute on the links; this uses the keyword lightbox, followed by a name for the lightbox in square brackets. This lightbox is called Japan.

Inside the link, you can have anything. There is just some simple text in this example, although you could include a thumbnail image to represent each of the larger images.

You do not have to indicate the size of the image, as the script will automatically determine this and resize the lightbox to fit the image.

You can also have multiple lightboxes on the same page as long as you give each lightbox a different name; here you can see a second lightbox added with photographs from Paris:

<h1>Images from Japan</h1>
<a href="images/Japan1.jpg" rel="lightbox[Japan]">Picture 1</a>
<a href="images/Japan2.jpg" rel="lightbox[Japan]">Picture 2</a>
<a href="images/Japan3.jpg" rel="lightbox[Japan]">Picture 3</a>
<h1>Images from Paris</h1>
<a href="images/Paris1.jpg" rel="lightbox[Paris]">Picture 1</a>
<a href="images/Paris2.jpg" rel="lightbox[Paris]">Picture 2</a>
<a href="images/Paris3.jpg" rel="lightbox[Paris]">Picture 3</a>

This is just one of many examples of lightbox scripts you would find if you searched for a lightbox script (other popular examples include Thickbox, Fancybox, and JQuery Lightbox).

This is a similar technique you may have seen used to create modal dialog boxes.

Creating a Modal Window

A modal window is a "child" window that you have to interact with before you can go back to the main window. You have probably seen modal windows on web sites you have visited; they are often used for login or contact forms, and their appearance is similar to the lightbox that you just saw (with a grayed-out page). You can see the example we are going to create in Figure 12-19.

Figure 12-19

Figure 12.19. Figure 12-19

The example of a modal window we will look at uses a script called Thickbox written by Cody Lindley; this example is built on another of the main JavaScript libraries called JQuery.

To re-create this example, first you need to include the JQuery library along with the Thickbox script and style sheet in the head of your document like so (ch12_eg21.html):

<script type="text/javascript"
    src="scripts/jquery/jquery-1.3.2.js"></script>
<script type="text/javascript" src="scripts/thickbox/thickbox.js"></script>
<link rel="stylesheet" href="scripts/thickbox/thickbox.css"
    type="text/css" media="screen" />

Next, you can write the content that you want to appear in the modal window:

<div id="myOnPageContent"><p>
  Here is a message that will appear when the user opens the modal window.
</p></div>

You do not want this to show when the page loads, so add the following style sheet rule into the header of the page (or your style sheet):

<style type="text/css">#myOnPageContent {display:none;}</style>

Finally, you can add a link that will open up the modal window. To do this, we will use an <a> element, although you could trigger it using the onload event of the document (to bring the window up when the page first loads), or you could use a form element.

There are a few things to note about this <a> element, starting with the class attribute whose value is thickbox:

<a class="thickbox"
  href="#TB_inline?height=300&width=500&inlineId=myOnPageContent"
  title="add a caption to title attribute / or leave blank">link</a>

As you can see, the value of the href attribute is quite long. Let's break that down:

  • #TB_inline? is used to trigger the lightbox.

  • height and width specify the height and width of the box in pixels. You can adjust these depending on the size of your message.

  • inlineID is used to specify the value of the id attribute of the element that contains the message to appear in the modal window. In our case, the <div> element containing the message for the modal window was myOnPageContent.

You may also have noticed that the link used a title attribute whose value appeared as a title at the top of the modal window.

Again this demonstrates how you can add a powerful technique to your page with very little code.

Sortable Tables with MochiKit

In this example, you will create a sortable table, which means you can re-order the contents of the table by clicking on the heading for each column. This kind of feature is particularly helpful when dealing with a long table where different users might want to sort the data in different ways (according to different column headings).

Figure 12-20 illustrates a table of employees; the up arrow next to the "Date started" table heading indicates that the table's contents are being ordered by the date the employee started (in ascending order). If you clicked on the heading "Name" you would be able to sort the table by alphabetical order of employee names.

Figure 12-20

Figure 12.20. Figure 12-20

This example uses another JavaScript library — MochiKit. By looking at examples that use different libraries, you can see how easy it is to work with the various libraries (which offer different functionality). You can download the latest version of MochiKit from www.mochikit.com/, although I have included version 1.3.1 with the download code for this chapter.

In order to create a sortable table, you again need to include two scripts; the first is for the MochiKit.js JavaScript library, and the second is for the sortable_tables.js file (ch12_eg22.html).

<script type="text/javascript"
   src="scripts/MochiKit/MochiKit.js"></script>
<script type="text/javascript"
  src="scripts/MochiKit/examples/sortable_tables/sortable_tables.js">
</script>

Next, I have added a couple of CSS styles to distinguish the headers from the columns and to set the font used:

<style type="text/css">
  th, td {font-family:arial, verdana, sans-serif;}
  th {background-color:#000000;width:200px;color:#ffffff;}
</style>

Now let's look at the actual table; there are just three things you need to add to your table so that you can sort the contents by clicking on the headings:

  • The <table> element needs an id attribute whose value is sortable_table.

  • For each column, the <th> (table heading) elements need to have an attribute called mochi:sortcolumn (we will look at the values this attribute takes after you have seen the code).

  • The first row of <td> elements needs to have mochi:content (again we will look at the values for this attribute after you have seen the code).

So here is the table with these additions:

<table id="sortable_table" class="datagrid">
  <thead>
    <tr>
      <th mochi:sortcolumn="name str">Name</th>
      <th mochi:sortcolumn="department str">Department</th>
      <th mochi:sortcolumn="datestarted isoDate">Date started</th>
      <th mochi:sortcolumn="extension str">Employee ID</th>
    </tr>
  </thead>
  <tbody>
    <tr mochi:repeat="item domains">
      <td mochi:content="item.name">Tim Smith</td>
      <td mochi:content="item.department">IT</td>
      <td mochi:content="item.datestarted">2007-02-10</td>
      <td mochi:content="item.extension">12</td>
    </tr>
    <tr>
      <td>Claire Waters</td>
      <td>Finance</td>
      <td>2006-09-24</td>
      <td>24</td>
    </tr>
    <tr>
      <td>Hetal Patel</td>
      <td>HR</td>
      <td>2006-01-10</td>
      <td>05</td>
    </tr>
    <tr>
      <td>Mark Whitehouse</td>
      <td>Sales</td>
      <td>2007-03-28</td>
      <td>09</td>
    </tr>
  </tbody>
</table>

The <th> elements carry the mochi:sortcolumn attribute, which contains two items of information separated by a space:

  • A unique ID for that column.

  • The format of the data in that column. This can be str if the data is a string or isoDate for a date in the format shown.

Now take a look at the first row of data because the <td> elements in this row carry mochi:content attributes. Again these are made up of two items, this time separated by a period or full stop:

  • The keyword item

  • The unique ID for the column that was specified in the mochi:sortcolumn attribute in the corresponding header

As with the other examples in this section, it is best to try it using the download code so you can see how it works and understand how easy it can be to add quite complex functionality to a table — creating an effect similar to the Sort Data options in Excel, which are useful when dealing with large amounts of data.

Creating Calendars with YUI

The fourth and final JavaScript library you will be looking at is the Yahoo User Interface library; it is the largest of the three libraries, with all kinds of functionality split into many separate scripts. I have only included a subset of version 2.7.0 of the YUI library with the code download for this chapter (the full version is over 11MB in size); however, you can download the full and latest version from http://developer.yahoo.com/yui/. Broadly speaking, the YUI is split into four sections:

  • At the heart of the YUI there are three scripts: the Yahoo Global Object, the DOM Collection, and the Event Utility. When using the library, sometimes you will only need to include one or two of these scripts; other times you will need all three.

  • Then there are library utilities, which provide functionality that you might use in many tasks, such as libraries that help you create animated effects, drag-and-drop functionality, and image loading. When one of these scripts requires functionality from one of the core scripts, you are told in the accompanying documentation.

  • At the other end of the spectrum, the library contains scripts to create individual kinds of user interface controls, such as calendars, color pickers, image carousels, and a text editor. There are helpful "cheat sheets" that show you how to quickly create each of these user interface components.

  • Finally, there is a set of CSS style sheets, which you might need to use with some of the UI controls in order to make them appear as you want.

In this section, we are going to see how to use the YUI to add a Calendar to your web page with just a few lines of code. Figure 12-21 shows what the calendar will look like.

Figure 12-21

Figure 12.21. Figure 12-21

To start, you have to include three core JavaScript files from the YUI library:

<script type="text/javascript" src="scripts/yui/yahoo/yahoo.js"></script>
<script type="text/javascript" src="scripts/yui/event/event.js" ></script>
<script type="text/javascript" src="scripts/yui/dom/dom.js" ></script>

Next, you need to add the calendar.js script, which is used to create the calendar (ch12_eg23.html).

<script type="text/javascript"
  src="scripts/yui/build/calendar/calendar.js"></script>

For this example, you will also include one of the css files that is included with the YUI download:

<link type="text/css" rel="stylesheet"
href="scripts/YUI/skins/sam/calendar.css">

In the body of the page, you need to add a <div> element, which will be populated by the calendar.

<div id="cal1Container"></div>

Finally, you add in the script, which calls the YUI library, and fills the <div> element with the calendar.

<script>
    YAHOO.namespace("example.calendar");
      YAHOO.example.calendar.init = function() {
      YAHOO.example.calendar.cal1 =
        new YAHOO.widget.Calendar("cal1","cal1Container");
      YAHOO.example.calendar.cal1.render();
    }
  YAHOO.util.Event.onDOMReady(YAHOO.example.calendar.init);
</script>

Rather like some of the other examples in this section, this is likely to be tied into some other kind of functionality, such as a holiday booking form where you are specifying dates you want to travel or an events list where you are looking at what is happening on a particular date. But this does demonstrate how libraries can be used to add significant functionality to your pages with ease.

Auto-Completing Text Inputs with YUI

The final example you will look at in this section allows you to create a text input where users are offered suggestions of options they might be trying to type. The example allows you to enter the name of a U.S. state, and as you start typing suggestions will appear as to which state you are trying to enter.

You can see what the input will look like in Figure 12-22.

Figure 12-22

Figure 12.22. Figure 12-22

To start with in this example (ch12_eg24.html), you include the three core JavaScript files:

<script type="text/javascript" src="scripts/yui/yahoo/yahoo.js"></script>
<script type="text/javascript" src="scripts/yui/event/event.js"></script>
<script type="text/javascript" src="scripts/yui/dom/dom.js"></script>

Then you add the animation and data source library utilities.

<script type="text/javascript"
    src="scripts/yui/animation/animation.js"></script>
<script type="text/javascript"
    src="scripts/yui/datasource/datasource.js"></script>

Finally, you add in the autocomplete.js JavaScript file:

<script type="text/javascript"
    src="scripts/yui/automcomplete/automcomplete.js"></script>

Then, in the body of the page, you add the text input and a <div> that will contain suggestions of what you are trying to type in.

Select a US state:
<input id="myInput" type="text">
<div id="myContainer"></div>

Next, a JavaScript array is created with all of the possibilities that someone might be trying to enter.

<script type="text/javascript">
YAHOO.example.arrayStates = [
    "Alabama",
    "Alaska",
    "Arizona",
    "Arkansas",
    "California",
    "Colorado",
    // other states go here
];
</script>

Finally, the JavaScript is added to the page that ties the text input form control to the array, and calls the Auto-Complete function so that the suggestions are made as users enter their cursors into the text input.

<script type="text/javascript">
YAHOO.example.ACJSArray = new function() {
    // Instantiate first JS Array DataSource
    this.oACDS = new YAHOO.widget.DS_JSArray(YAHOO.example.statesArray);
    // Instantiate first AutoComplete
    this.oAutoComp =
      new YAHOO.widget.AutoComplete('statesinput','myContainer',
        this.oACDS);
    this.oAutoComp.prehighlightClassName = "yui-ac-prehighlight";
    this.oAutoComp.typeAhead = true;
    this.oAutoComp.useShadow = true;
    this.oAutoComp.minQueryLength = 0;
    this.oAutoComp.textboxFocusEvent.subscribe(function(){
        var sInputValue = YAHOO.util.Dom.get('statesinput').value;
        if(sInputValue.length === 0) {
            var oSelf = this;
            setTimeout(function(){oSelf.sendQuery(sInputValue);},0);
        }
    });

};
</script>

Again, you can see that by following a simple example made available with a JavaScript toolkit, you can significantly enhance the usability or functionality of your page (without the need to write all of the code to do the job from scratch).

There are many more JavaScript libraries on the Web, each of which has different functionality, and each of which is continually being developed and refined, so it is worthwhile taking some time to look at the different libraries that are available, and checking in with your favorites every so often to see how they have been updated.

Summary

In this chapter, you have seen many uses for JavaScript, and you should now have a better understanding of how to apply it. With the help of these scripts you should now be able to use these and other scripts in your page. You should also have an idea of how you can tailor or even write your own scripts.

You have seen how you can help a user fill in a form correctly by providing validation. For example, you might check to make sure required fields have something in them or that an e-mail address follows the expected pattern. This saves users time by telling them what they have to do before a page gets sent to a server, processed, and then returned with errors. The validation examples highlight the access the DOM gives to document content, so that you can perform operations on the values users provide.

You also saw how the DOM can help make a form generally more usable by putting the focus on appropriate parts of the form and manipulating the text users have entered, by removing or replacing certain characters.

Finally, you took a look at some popular JavaScript libraries: Scriptaculous, JQuery, MochiKit, and the Yahoo User Interface Library. JavaScript libraries offer sophisticated functionality that you can easily drop into your pages with just a few lines of code, and are the basis for many of the scripts available on the web that allow you to add complex functionality to your site with minimum effort.

Exercises

There is only one exercise for this chapter because it is quite a long one. The answers to all the exercises are in Appendix A.

  1. Your task is to create a validation function for the competition form in Figure 12-23.

The function should check that the user has done the following things:

  • Entered his or her name

  • Provided a valid e-mail address

  • Selected one of the radio buttons as an answer to the question

  • Given an answer for the tiebreaker question, which is no more than 20 words

These should be in the order that the controls appear on the form.

Figure 12-23

Figure 12.23. Figure 12-23

Here is the code for the form:

<form name="frmCompetition" action="competition.aspx" method="post"
onsubmit="return validate(this);">
<h2>An Example Competition Form <br />(Sorry, there are no real
prizes!)</h2>
<p> To enter the drawing to win a case of Jenny's Jam, first answer
this question: "What color are strawberries?" Then provide an answer for
the tie-breaker question: "I would like to win a case of Jenny's Jam
because..." in no more than 20 words.</p>
<table>
  <tr>
    <td class="formTitle">Name: </td>
    <td><input type="text" name="txtName" size="18" /></td>
  </tr>
  <tr>
    <td class="formTitle">Email: </td>
    <td><input type="text" name="txtEmail" size="18" /></td>
  </tr>
  <tr>
    <td class="formTitle">Answer: </td>
    <td><input type="radio" name="radAnswer" value="Red" /> Red<br />
          <input type="radio" name="radAnswer" value="Gray" /> Gray<br />
          <input type="radio" name="radAnswer" value="Blue" /> Blue
    </td>
    </tr>
   <tr>
     <td class="formTitle">Tie breaker <br/ ><small>(no more than 20 words)
     </small>: </td>
     <td><textarea name="txtTieBreaker" cols="30" rows="3"/></textarea>
     </td>
   </tr>
   <tr>
     <td class="formTitle"></td>
     <td><input type="submit" value="Enter now" /></td>
   </tr>
</table>
</form>
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.146.77