About ten elements are commonly found within <form> elements. The most useful are shown in Figures 6-1, 6-2, 6-3, and 6-4, ordered into general types. We give each its name and, in parentheses, the HTML needed to create it, though note this is not the full HTML but only a portion.
As you can see, most of the form elements are created using the <input> tag. One of the <input> tag's attributes is the type attribute. It's this attribute that decides which of the HTML elements this will be. Examples of values for this attribute include button (to create a button) and text (to create a text box).
Each form element inside the web page is made available to us as—yes, you guessed it—an object. As with all the other objects we have seen, each element's object has its own set of distinctive properties, methods, and events. We'll be taking a look at each form element in turn and how to use its particular properties, methods, and events, but before we do that, let's look at properties and methods that the objects of the form elements have in common.
One property that all the objects of the form elements have in common is the name property. We can use the value of this property to reference that particular element in our script. Also, if we are sending the information in the form to a server, the element's name property is sent along with any value of the form element, so that the server knows what the value relates to.
Most form element objects also have the value property in common, which returns the value of the element. For example, for a text box, the value property returns the text that the user has entered in the text box. Also, setting the value of the value property allows us to put text inside the text box. However, the use of the value property is specific to each element, so we'll look at what it means as we look at each individual element.
All form element objects also have the form property, which returns the Form object in which the element is contained. This can be useful in cases where you have a generic routine that checks the validity of data in a form. For example, when the user clicks a submit button, we can pass the Form object referenced by the form property to our data checker, which can use it to loop through each element on the form in turn, checking that data in the element is valid. This is handy if you have more than one form defined on the page or where you have a generic data checker that you cut and paste to different pages—this way you don't need to know the form's name in advance.
Sometimes it's useful to know what type of element you're dealing with, particularly where you're looping through the elements in a form using the elements[] array property of the Form object. This information can be retrieved using the type property, which each element's object has. This property returns the type of the element (for example, button or text).
All form element objects also have the focus() and blur() methods. Focus is a concept you might not have come across yet. If an element is the center of the focus, any key presses made by the user will be passed directly to that element. For example, if a text box has focus, pressing keys will enter values into the text box. Also, if a button has the focus, pressing the Enter key will cause the button's onclick event handler code to fire, just as if a user had clicked the button with his mouse.
The user can set which element currently has the focus by clicking on it or by using the Tab key to select it. However, we as the programmer can also decide which element has the focus by using the form element's object's focus() method. For example, if we have a text box for the user to enter his age and he enters an invalid value, such as a letter rather than a number, we can tell him that his input is invalid and send him back to that text box to correct his mistake.
Blur, which perhaps could be better named "lost focus," is the opposite of focus. If we want to remove a form element from being the focus of the user's attention, we can use the blur() method. When used with a form element, the blur() method usually results in the focus shifting to the page containing the form.
In addition to the focus() and blur() methods, all the form element's objects have the onfocus and onblur event handlers. These are fired, as you'd expect, when an element gets the focus or loses the focus, due to user action or the focus() and blur() methods. The onblur event handler can be a good place to check the validity of data in the element that has just lost the focus. If invalid, you can set the focus back to the element and let the user know why the data he entered is wrong.
One thing to be careful of is using the focus() or blur() methods in the onfocus or onblur event handler code. There is a danger of an infinite loop occurring. For example, consider two elements, each of whose onfocus events passes the focus to the other element. Then, if one element gets the focus, its onfocus event will pass the focus to the second element, whose onfocus event will pass the focus back to the first element, and so on until the only way out is to close the browser down. This is not likely to please your users!
Also be very wary of using the focus() and blur() methods to put focus back in a problem field if that field or others depend on some of the user's input. For example, say we have two text boxes, one in which we want the user to enter her city and the other in which we want her to enter her state. Also say that the input into the state text box is checked to make sure that the specified city is in that state. If the state does not contain the city, we put the focus back on the state text box so that the user can change the name of the state. However, if the user actually input the wrong city name and the right state name, she may not be able to go back to the city text box to rectify the problem.
I'm starting our look at form elements with the standard button element because it's probably the most commonly used and is fairly simple. The HTML tag to create a button is the <input> tag. For example, to create a button called myButton, which has the words "Click Me" on its face, the <input> tag would need to be
<input type="button" name="myButton" value="Click Me">
The type attribute is set to button, and the value attribute is set to the text we want to appear on the face of the button. We can leave the value attribute off, but we'll end up with a blank button, which will leave our users guessing as to its purpose.
This element creates an associated Button object; in this example it is called myButton. This object has all the common properties and methods described earlier, including the value property. This allows you to change the text on the button face using JavaScript, though this is probably not something you'll need to do very often. What the button is really all about is the onclick event.
We connect to the button's onclick event handler just as we did for the onclick events of other HTML tags such as the <A> tag. All we need do is to define a function that we want to execute when the button is clicked (say, button_onclick()) and then add the onclick event handler as an attribute of the <input> tag as follows:
<input type="button" onclick="button_onclick()">
In the following example we use the methods described previously to record how often a button has been clicked.
<html> <head> <script language=JavaScript> var numberOfClicks = 0; function myButton_onclick() { numberOfClicks++; window.document.form1.myButton.value = 'Button clicked ' + numberOfClicks + ' times'; } </script> </head> <body> <form name=form1> <input type='button' name='myButton' value='Button clicked 0 times' onclick="myButton_onclick()"> </form> </body> </html>
Save this page as ch6_examp2.htm. If you load this page into your browser, you will see a button with "Button clicked 0 times" on it. On repeatedly pressing this button, you will see the number of button clicks recorded on the top of the button.
We start the script block in the head of the page by defining a global variable, accessible anywhere inside our page, called numberOfClicks. We record the number of times the button has been clicked in this variable, and use this information to update the button's text.
The other piece of code in the script block is the definition of the function myButton_onclick(). This function is connected to the onclick event handler in the <input> tag in the body of the page. This tag is for a button element called myButton and is contained within a form called form1.
<form name=form1> <input type='button' name='myButton' value='Button clicked 0 times' onclick="myButton_onclick()"> </form>
Let's look at the myButton_onclick() function a little more closely. First, the function increments the value of the variable numberOfClicks by one.
function myButton_onclick() { numberOfClicks++;
Next, we update the text on the button face using the Button object's value property.
window.document.form1.myButton.value = 'Button clicked ' + numberOfClicks + ' times'; }
The function is specific to this form and button, rather than a generic function we'll be using in other situations. Therefore I've referred to the form and button directly using window.document.form1.myButton. Remember that the window object has a property containing the document object, which itself holds all the elements in a page, including the <form> element, and that the button is embedded inside our form.
Two less commonly used events supported by the Button object in version 4 or higher browsers are the onmousedown and onmouseup events. You can see these two events in action in the next example.
<html> <head> <script language=JavaScript> function myButton_onmouseup() { document.form1.myButton.value = "Mouse Goes Up" } function myButton_onmousedown() { document.form1.myButton.value = "Mouse Goes Down" } </script> </head> <body> <form name=form1> <input type='button' name='myButton' value=' Mouse Goes Up ' onmouseup="myButton_onmouseup()" onmousedown="myButton_onmousedown()"> </form> </body> </html>
Save this page as ch6_examp3.htm and load it into your browser. If you click the button with your left mouse button and keep it held down, you'll see the text on the button change to "Mouse Goes Down." As soon as you release the button, the text changes to "Mouse Goes Up."
In the body of the page we define a button called myButton within a form called form1. Within the attributes of the <input> tag, we attach the function myButton_onmouseup() to the onmouseup event handler, and the function myButton_onmousedown() to the onmousedown event handler.
<form name=form1> <input type='button' name='myButton' value=' Mouse Goes Up ' onmouseup="myButton_onmouseup()" onmousedown="myButton_onmousedown()"> </form>
The myButton_onmouseup() and myButton_onmousedown() functions are defined in a script block in the head of the page. Each function consists of just a single line of code, in which we use the value property of the Button object to change the text that is displayed on the button's face.
An important point to note is that events like onmouseup and onmousedown only trigger when the mouse pointer is actually over the element in question. For example, if you click and hold down the mouse button over our button, then move the mouse away from the button before releasing the mouse button, you'll find that the onmouseup event does not fire and the text on the button's face does not change. In this instance it would be the document object's onmouseup event handler code that would fire, if we'd connected any code to it.
Don't forget that, like all form element objects, the Button object also has the onfocus and onblur events, though they are rarely used in the context of buttons.
Two additional types of button are the submit and reset buttons. Defining the submit and reset buttons is done in the same way as defining a standard button, except that the type attribute of the <input> tag is set to submit or reset rather than button. For example, the submit and reset buttons in Figure 6-4 were created using
<input type="submit" value="Submit" name="submit1"> <input type="reset" value="Reset" name="reset1">
These buttons have special purposes, which are not related to script.
When the submit button is clicked, the form data from the form that the button is inside gets automatically sent to the server, without the need for any script.
When the reset button is clicked, all the elements in a form are cleared and returned to their default values; the values they had when the page was first loaded.
The submit and reset buttons have corresponding objects called Submit and Reset, which have exactly the same properties, methods, and events as a standard Button object.
The standard text element allows users to enter a single line of text. This information can then be used in JavaScript code or submitted to a server for server-side processing.
A text box is created using the <input> tag, much as our button was, but by setting the type attribute to text. Again, you can choose not to include the value attribute, but if you do include it this value will appear inside the text box when the page is loaded.
In the following example, the <input> tag has two additional attributes of size and maxlength. The size attribute determines how many characters wide the text box is, and maxlength determines the maximum number of characters the user can enter into the box. Both attributes are optional and use defaults determined by the browser.
For example, to create a text box 10 characters wide, with a maximum character length of 15, and initially containing the words 'Hello World', our <input> tag would be as follows:
<input type="text" name="myTextBox" size=10 maxlength=15 value="Hello World">
The Text object that this element creates has a value property, which we can use in our scripting to set or read the text contained inside the text box. In addition to the common properties and methods we discussed earlier, the Text object also has the select() method, which selects or highlights all the text inside the text box. This may be used if the user has entered an invalid value, and we can set the focus to the text box and select the text inside it. This then puts the user's cursor in the right place to correct the data and makes it very clear to the user where the invalid data is. The value property of Text objects always returns a string data type, even if number characters are being entered. If we use the value as a number, JavaScript normally does a conversion from a string data type to a number data type for us, but this is not always the case. For example, JavaScript won't do the conversion if the operation we're doing is valid for a string. If we have a form with two text boxes and we added the values returned from these, JavaScript concatenates rather than adds the two values, so 1 + 1 will be 11 and not 2. To fix this, we need to convert all the values involved to a numerical data type, for example by using parseInt() or parseFloat() or Number(). However, if we subtracted the two values, an operation only valid for numbers, JavaScript says "ah ha this can only be done with numbers so I'll convert the values to a number data type." So, 1 – 1 will be returned as 0 without using parseInt() or parseFloat(). This is a tricky bug to spot, so it's best to get into the habit of converting explicitly to save problems later.
In addition to the common event handlers, such as onfocus and onblur, the Text object has the onchange, onselect, onkeydown, onkeypress, and onkeyup event handlers.
The onselect event fires when the user selects some text in the text box.
More useful is the onchange event, which fires when the element loses focus if (and only if) the value inside the text box is different from the value it had when it got the focus. This enables us to do things like validity checks that occur only if something has changed.
As mentioned before, the onfocus and onblur events can be used for validating user input. However, they also have another purpose, and that's to make a text box read-only. In IE 4.0+ and NN 6 we can use the READONLY attribute of the <input> tag or the readOnly property of the Text object to prevent the contents from being changed. However, this won't work on NN 4.x. We can get around this using the blur() method. All we need to do is add an onfocus event handler to the <input> tag defining the text box and connect it to some code that blurs the focus from the text box with the blur() method.
<input type="text" name=txtReadonly value="Look but don't change" onfocus="window.document.form1.txtReadonly.blur()" READONLY=true>
The onkeypress, onkeydown, and onkeyup events fire, as their names suggest, when the user presses a key, when the user presses a key down, and when a key pressed down is let back up.
Let's put all the information on text boxes and buttons together into an example. In this example we have a simple form consisting of two text boxes and a button. The top text box is for the user's name, and the second is for her age. We do various validity checks. We check the validity of the age text box when it loses focus. However, the name and age text boxes are only checked to see if they are empty when the button is clicked.
<html> <head> <script language=JavaScript> function butCheckForm_onclick() { var myForm = document.form1; if (myForm.txtAge.value == "" || myForm.txtName.value == "") { alert("Please complete all the form"); if (myForm.txtName.value == "") { myForm.txtName.focus(); } else { myForm.txtAge.focus(); } } else { alert("Thanks for completing the form " + myForm.txtName.value); } } function txtAge_onblur() { var txtAge = document.form1.txtAge; if (isNaN(txtAge.value) == true) { alert("Please enter a valid age"); txtAge.focus(); txtAge.select(); } } function txtName_onchange() { window.status = "Hi " + document.form1.txtName.value; } </script> </head> <body> <form name=form1> Please enter the following details: <p> Name: <br> <input type="text" name=txtName onchange="txtName_onchange()"> <br> Age: <br> <input type="text" name=txtAge onblur="txtAge_onblur()" size=3 maxlength=3> <br> <input type="button" value="Check Details" name=butCheckForm onclick="butCheckForm_onclick()"> </form> </body> </html>
After you've entered the text, save the file as ch6_examp4.htm and load it into your web browser.
In the text box shown in Figure 6-5, type your name. When you leave the text box you'll see "Hi yourname" appear in the status bar at the bottom of the window.
Enter an invalid value into the age text box, such as "aaaa," and when you try to leave the box, it'll tell you of the error and send you back to correct it.
Finally, click the Check Details button and both text boxes will be checked to see that you have completed them. If either is empty, you'll get a message telling you to complete the whole form, and it'll send you back to the box that's empty.
If everything is filled in correctly, you'll get a message thanking you.
Note that this example does not work properly on NN 6 or later browsers, and we'll discuss why shortly.
Within the body of the page, we create the HTML tags that define our form. Inside our form, which is called form1, we create the three form elements with the names txtName, txtAge, and butCheckForm.
<form name=form1> Please enter the following details: <p> Name: <br> <input type="text" name=txtName onchange="txtName_onchange()"> <br> Age: <br> <input type="text" name=txtAge onblur="txtAge_onblur()" size=3 maxlength=3> <br> <input type="button" value="Check Details" name=butCheckForm onclick="butCheckForm_onclick()"> </form>
You'll see that for the second text box (the txtAge text box), we have included the size and maxlength attributes inside the <input> tag. Setting the size attribute to 3 gives the user an idea of how much text we are expecting, and setting the maxlength attribute to 3 helps ensure that we don't get overly large numbers entered for our age value.
The first text box's onchange event handler is connected to the function txtName_onchange(), the second text box's onblur event handler is connected to the function txtAge_onblur(), and the button's onclick event handler is connected to the function butCheckForm_onclick(). These functions are defined in a script block in the head of the page. We will look at each of them in turn, starting with butCheckForm_onclick().
The first thing we do is define a variable, myForm, and set it to reference the Form object created by our <form> tag later in the page.
function butCheckForm_onclick() { var myForm = document.form1;
Doing this reduces the size of our code each time we want to use the form1 object. Instead of document.form1 we can just type myForm. It makes our code a bit more readable and therefore easier to debug, and it saves typing. When we set a variable to be equal to an existing object, we don't (in this case) actually create a new form1 object. Instead we just point our variable to the existing form1 object. So when we type myForm.name, JavaScript checks our variable, finds it's actually storing the location in memory of the object form1, and uses that object instead. All this goes on behind the scenes so we don't need to worry about it and can just use myForm as if it was document.form1.
After getting our reference to the Form object, we then use it in an if statement to check whether the value in the text box named txtAge or the text box named txtName actually contains any text.
if (myForm.txtAge.value == "" || myForm.txtName.value == "") { alert("Please complete all the form"); if (myForm.txtName.value == "") { myForm.txtName.focus(); } else { myForm.txtAge.focus(); } }
If we do find an incomplete form, we alert the user. Then in an inner if statement, we check which text box was not filled in. We set the focus to the offending text box, so that the user can start filling it in straight away without having to move the focus to it herself. It also lets the user know which text box our program requires to be filled in. To avoid annoying your users, make sure that text in the page tells them which fields are required.
If the original outer if statement found that the form was complete, it would let the user know with a thank you message.
else { alert("Thanks for completing the form " + myForm.txtName.value); } }
In this sort of situation, it's probably more likely that at this point, having validated the form, we'd submit it to the server for processing. We can do this using the Form object's submit() method, as we'll see in the chapters on server-side programming, or with a normal submit button.
The next of our three functions is txtAge_onblur(), which is connected to the onblur event of our txtAge text box. The purpose of this function is to check that the string value the user entered into the age box actually consists of number characters.
function txtAge_onblur() { var txtAge = document.form1.txtAge;
Again at the start of the function, we declare a variable and set it to reference an object; this time it's the Text object created for the txtAge text box that we define further down the page. Now instead of having to type document.form1.txtAge every time, we just type txtAge, and it acts as the same thing. It certainly helps save those typing fingers, especially since it's a big function with multiple use of the txtAge object.
The following if statement checks to see whether what has been entered in the txtAge text box can be converted to a number. We use the isNaN() function to do this for us. If the value in txtAge text box is not a number, it's time to tell the user and set the focus back to the offending element with the focus() method of the corresponding Text object. Additionally, this time we highlight the text by using the Text object's select() method. This makes it even clearer to the user, and he can rectify the problem without needing to delete text first.
if (isNaN(txtAge.value) == true) { alert("Please enter a valid age"); txtAge.focus(); txtAge.select(); } }
We could go further and check that the number inside the text box is actually a valid age, for example—191 is not a valid age, nor is 255 likely to be. We just need to add another if statement to check for these possibilities, but I'll leave that as an extra exercise.
This function is connected to the onblur event handler of the txtAge text box, but why didn't we use the onchange event handler, with the advantage that we only recheck the value when it's actually been changed? The onchange event would not fire in the situation where the box was empty before focus was passed to it, and after focus was passed away from it. However, leaving the checking of the form completion until just before the form is submitted is probably better because some users prefer to fill in information out of order and come back to some form elements later.
The final function is for the txtName text box's onchange event. Its use here is a little flippant and more as an example of the onchange event.
function txtName_onchange() { window.status = "Hi " + document.form1.txtName.value; }
When the onchange event fires (when focus is passed away from the name text box and its contents have changed), we take the value of the txtName box and put it into the window's status bar at the bottom of the window. It simply says "Hi yourname." We access the status bar using the window object's status property, although we could just put the following:
status = "Hi " + document.form1.txtName.value;
I've actually put window in front of this just to make it clear what we are actually accessing. It would be very easy when reading the code to mistake status for a variable, so in this situation, although strictly unnecessary, putting window in front does make the code easier to read, understand, and therefore debug.
The previous example will fail with NN 6+ if we enter a name in the name text box and then an invalid age into the age box (for example, entering abc and then clicking the Check Form button). With IE and NN 4, the blur event fires and displays an alert box if the age is invalid; however, the button's click event doesn't fire. However, in NN 6+, both events fire with the result that the invalid age alert is hidden by the form completed successfully alert box.
In addition, if we enter an invalid age for both IE and Netscape browsers and then switch to a different program altogether, the invalid age alert box appears, which is annoying for the user. It could be the user was opening up another program to check the details.
Although this is a fine example, it is not great for the real world. A better option would be to check the form when it's finally submitted and not while the user is entering data. Or alternatively we could check the data as it's entered but not use an alert box to display errors. Instead we could write out a warning in red next to the erroneous input control informing the user of the invalid data, and then also get our code to check the form when it's submitted. We'll see how to write to the page later after it's been loaded in the dynamic HTML chapters.
The only real purpose of the password box is to allow users to type in a password on a page and to have the password characters hidden, so that no one can look over the user's shoulder and discover his or her password. However, when sent to the server, the text in the password is sent as plain text—there is no encryption or any attempt at hiding the text—so it's not a secure way of passing information.
Defining a password box is identical to a text box, except that the type attribute is password.
<input name=password1 type=password>
This form element creates an associated Password object, which is almost identical to the Text object in its properties, methods, and events.
The hidden text box can hold text and numbers just like a normal text box, with the difference being that it's not visible to the user. A hidden element? It may sound as useful as an invisible painting, but in fact it proves to be very useful.
To define a hidden text box, we have the following HTML:
<input type="hidden" name=myHiddenElement>
The hidden text box creates a Hidden object. This is available in the elements array property of the Form object and can be manipulated in JavaScript like any other object, although we can actually set its value only through its HTML definition or through JavaScript. Like a normal text box, its value is submitted to the server when the user submits the form.
So why are hidden text boxes useful? Let's imagine we have a lot of information that we need to obtain from the user, but to avoid having a page stuffed full of elements and looking like the control panel of the space shuttle, we decide to obtain the information over more than one page. The problem is, how do we keep a record of what was entered in previous pages? Easy—we use hidden text boxes and put the values in there. Then, in the final page, all the information is submitted to the server—it's just that some of it is hidden. Anyway, we'll see more about this in Chapter 16.
The textarea element allows multi-line input of text. Other than this, it acts very much like the text box element.
However, unlike the text box, the textarea element has its own tag, the <textarea> tag. It also has two additional attributes: COLS and ROWS. The COLS attribute defines how many characters wide the textarea will be, and the ROWS attribute defines how many character rows there will be. Setting the text inside the element is done by putting it between the start and close tags, rather than by using the value attribute. So if we want a textarea element 40 characters wide by 20 rows deep with initial text of "Hello World" on the first line and "Line 2" on the second line, we would define this as follows:
<textarea name=myTextArea cols=40 rows=20>Hello World Line 2 </textarea>
Another additional attribute of the <textarea> tag is the wrap attribute, which determines what happens when the user types to the end of a line. The default value for this is off so that if the user does not press Return at the end of a line, it just keeps going, with a horizontal scrollbar appearing, though this can vary from browser to browser. To turn wrapping on, we have two possible values we can use: nothing (this includes just the wrap attribute on its own) and hard. As far as client-side processing goes, both do the same thing: they switch wrapping on. However when we come to server-side processing, they do make a difference to which information is sent to the server when the form is posted.
If we set the wrap attribute on by including it in the tag definition, or by setting it to soft, wrapping will occur client side, but the carriage returns won't be posted to the server, just the text. If the wrap attribute is set to hard, any carriage returns caused by wrapping will be converted to hard returns—as if the user had pressed the Enter key, and these will be sent to the server. Also, we need to be aware that the carriage return character is determined by the operating system that the browser is running on—for example, in Windows a carriage return is \r\n, whereas on a Macintosh the carriage return is \r and on Unix a carriage return is \n.
The Textarea object created by the <textarea> tag has the same properties, methods, and events as the Text object we saw previously except the textarea doesn't have the maxlength attribute. Note that there is a value property even though the <textarea> tag does not have a value attribute. The value property simply returns the text between the <textarea> and </textarea> tags. The events supported by the Textarea object include the onkeydown, onkeypress, onkeyup, and onchange event handlers.
To help demonstrate how the keydown, keypress, keyup, and change events work (in particular, the order in which they fire), let's create an example that tells us what events are firing.
<html> <head> <script language=JavaScript> function DisplayEvent(eventName) { var myMessage = window.document.form1.textarea2.value; myMessage = myMessage + eventName; window.document.form1.textarea2.value = myMessage; } </script> </head> <body> <form name=form1> <textarea rows=15 cols=40 name=textarea1 onchange="DisplayEvent('onchange\n');" onkeydown="DisplayEvent('onkeydown\n');" onkeypress="DisplayEvent('onkeypress\n');" onkeyup="DisplayEvent('onkeyup\n\n');"></textarea> <textarea rows=15 cols=40 name=textarea2></textarea> <br><br> <input type="button" value="Clear Event TextArea" name=button1 onclick="window.document.form1.textarea2.value=''"> </form> </body> </html>
Save this page as ch6_examp5.htm. Load the page into your browser and see what happens when you type any letter into the first textarea box. You should see the events being fired listed in the second textarea box (onkeydown, onkeypress, and onkeyup) as shown in Figure 6-6. If you click outside of the first textarea box, you'll see the onchange event fire.
Experiment with the example to see what events fire and when. Sometimes you will not get quite the results you expect. For example, if you press and hold a key down, onkeydown and onkeypress will fire in IE and NN 6 and 7 continuously until you let go, when just one onkeyup event will fire. In NN 4.x, onkeydown will fire once and then onkeypress will fire continuously until you let go, when just one onkeyup event will fire.
Within a form called form1 in the body of the page, we define two textareas and a button. The first textarea is the one whose events we are going to monitor. We attach code that calls the DisplayEvent() function to each of the onchange, onkeydown, onkeypress, and onkeyup event handlers. The value passed to the function reflects the name of the event firing.
<textarea rows=15 cols=40 name=textarea1 onchange="DisplayEvent('onchange\n');" onkeydown="DisplayEvent('onkeydown\n');" onkeypress="DisplayEvent('onkeypress\n');" onkeyup="DisplayEvent('onkeyup\n\n');"></textarea>
Next we have an empty textarea the same size as the first textarea.
<textarea rows=15 cols=40 name=textarea2></textarea>
Finally we have our button element as follows:
<input type="button" value="Clear Event TextArea" NAME=button1 onclick="window.document.form1.textarea2.value=''">
Notice that the onclick event handler for the button is not calling a function, but just executing a line of JavaScript. Although we normally call functions, it's not compulsory; if we have just one line of code to execute, it's easier just to insert that rather than create a function and call it. In this case, the onclick event handler is connected to some code that sets the contents of the second textarea to empty ('').
Now let's look at the DisplayEvent() function. This is defined in a script block in the head of the page. It adds the name of the event handler that has been passed as a parameter to the text already contained in the second textarea.
function DisplayEvent(eventName) { var myMessage = window.document.form1.textarea2.value; myMessage = myMessage + eventName; window.document.form1.textarea2.value = myMessage; }
I've put the discussion of checkboxes and radio buttons together because their objects have identical properties, methods, and events. A checkbox allows the user to check and uncheck it. It is similar to the paper surveys you may get where you are asked to "check the boxes that apply to you." Radio buttons are basically a group of checkboxes, with the property that only one can be checked at once. Of course, they also look different, and their group nature means that they are treated differently.
Creating checkboxes and radio buttons requires our old friend the <input> tag. Its type attribute is set to "checkbox" or "radio" to determine which box or button is created. To set a checkbox or a radio button to be checked when the page is loaded, we simply insert the keyword checked into the <input> tag. This is handy if we want to set a default option like, for example, those "Check this box if you want our junk mail" forms you often see on the Net, which are usually checked by default, forcing us to uncheck them. So to create a checkbox that is already checked, our <input> tag will be the following:
<input type="checkbox" name=chkDVD checked value="DVD">
To create a checked radio button, the <input> tag would be as follows:
<input type="radio" name=radCPUSpeed checked value="1 GHz">
I mentioned that radio buttons are group elements. In fact, there is little point in putting just one on a page because the user won't be able to choose between any alternative boxes.
To create a group of radio buttons, we simply give each radio button the same name. This creates an array of radio buttons going by that name that we can access, as we would with any array, using its index.
For example, to create a group of three radio buttons, our HTML would be
<input type="radio" name=radCPUSpeed checked value="800 MHz"> <input type="radio" name=radCPUSpeed value="1 GHz"> <input type="radio" name=radCPUSpeed value="1.5 GHz">
We can have as many groups of radio buttons in a form as we want, by just giving each group its own unique name. Note that we have only used one CHECKED attribute, since only one of the radio buttons in the group can be checked. If we had used the CHECKED attribute in more than one of the radio buttons, only the last of these would have actually been checked.
Using the value attribute of the checkbox and radio button elements is different from previous elements we've looked at. First, it tells you nothing about the user's interaction with an element, because it's predefined in our HTML or by our JavaScript. Whether a checkbox or radio button is checked or not, it still returns the same value. Second, when a form is posted to a server, only the values of the checked checkboxes and radio buttons are sent. So, if you have a form with ten checkboxes and the user submits the form with none of these checked, then nothing would be sent to the server except a blank form. We'll learn more about this when we look at server-side scripting in Chapter 16.
Each checkbox has an associated Checkbox object, and each radio button in a group has a separate Radio object. As mentioned earlier, with radio buttons of the same name, we can access each Radio object in a group by treating the group of radio buttons as an array, with the name of the array being the name of the radio buttons in the group. As with any array, we have the length property, which will tell us how many radio buttons there are in that group.
For determining whether a user has actually checked or unchecked a checkbox, we need to use the checked property of the Checkbox object. This property returns true if the checkbox is currently checked and false if not.
Radio buttons are slightly different. Because radio buttons with the same name are grouped together, we need to test each Radio object in the group in turn to see if it has been checked. Only one of the radio buttons in a group can be checked, so if you check another one in the group, the previously checked one will become unchecked, and the new one will be checked in its place.
Both Checkbox and Radio have the event handlers onclick, onfocus, and onblur, and these operate as we saw for the other elements, although in addition they can also be used to cancel the default action, such as clicking the checkbox or radio button.
Let's look at an example that makes use of all the properties, methods, and events we have just talked about. The example is a simple form that allows a user to build a computer system. Perhaps it could be used in an e-commerce situation for buying computers online with the exact specification determined by the customer.
<html> <head> <script language=JavaScript> var radCpuSpeedIndex = 0; function radCPUSpeed_onclick(radIndex) { var returnValue = true; if (radIndex == 1) { returnValue = false; alert("Sorry that processor speed is currently unavailable"); // Next line works around a bug in IE that doesn't cancel the // Default action properly document.form1.radCPUSpeed[radCpuSpeedIndex].checked = true; } else { radCpuSpeedIndex = radIndex; } return returnValue; } function butCheck_onclick() { var controlIndex; var element; var numberOfControls = document.form1.length; var compSpec = "Your chosen processor speed is "; compSpec = compSpec + document.form1.radCPUSpeed[radCpuSpeedIndex].value; compSpec = compSpec + "\nWith the following additional components\n"; for (controlIndex = 0; controlIndex < numberOfControls; controlIndex++) { element = document.form1[controlIndex]; if (element.type == "checkbox") { if (element.checked == true) { compSpec = compSpec + element.value + "\n"; } } } alert(compSpec); } </script> </head> <body> <form name=form1> <p> Tick all of the components you want included on your computer <br><br> <table> <tr> <td>DVD-ROM</td> <td><input type="checkbox" name="chkDVD" value="DVD-ROM"></td> </tr> <tr> <td>CD-ROM</td> <td><input type="checkbox" name="chkCD" value="CD-ROM"></td> </tr> <tr> <td>Zip Drive</td> <td><input type="checkbox" name="chkZip" value="ZIP Drive"></td> </tr> </table> <p> Select the processor speed you require <table> <tr> <td><input type="radio" name="radCPUSpeed" checked onclick="return radCPUSpeed_onclick(0)" value="3.8 GHz"></td> <td>3.8 GHz</td> <td><input type="radio" name="radCPUSpeed" onclick="return radCPUSpeed_onclick(1)" value="4.8 GHz"></td> <td>4.8 GHz</td> <td><input type="radio" name="radCPUSpeed" onclick="return radCPUSpeed_onclick(2)" value="6 Ghz"></td> <td>6 GHz</td> </tr> </table> </p> <input type="button" value="Check Form" name="butCheck" onclick="return butCheck_onclick()"> </form> </body> </html>
Save the page as ch6_examp6.htm and load it into your web browser. You should see a form like the one shown in Figure 6-7.
Check some of the checkboxes, change the processor speed, and click the Check Form button. A message box will appear giving details of which components and what processor speed you selected. For example, if you select a DVD-ROM and a Zip Drive and 6-GHz processor speed, you will see something like that shown in Figure 6-8.
Note that the 4.8-GHz processor is out of stock, so if you choose that, a message box will appear telling you that it's out of stock, and the 4.8-GHz processor speed radio button won't be selected. The previous setting will be restored once the user dismisses the message box.
Let's first look at the body of the page, where we define the checkboxes, radio buttons, and a standard button inside a form called form1. We start with the checkboxes. They are put into a table simply for formatting purposes. No functions are called, and no events are linked to.
<table> <tr> <td>DVD-ROM</td> <td><input type="checkbox" name=chkDVD value="DVD-ROM"></td> </tr> <tr> <td>CD-ROM</td> <td><input type="checkbox" name=chkCD value="CD-ROM"></td> </tr> <tr> <td>Zip Drive</td> <td><input type="checkbox" name=chkZip value="ZIP Drive"></td> </tr> </table>
Next come the radio buttons for selecting the required CPU speed, and these are a little more complex. Again they are put into a table for formatting purposes.
<table> <tr> <td><input type="radio" name=radCPUSpeed checked onclick="return radCPUSpeed_onclick(0)" value="3.8 GHz"></td> <td>800 MHz</td> <td><input type="radio" name=radCPUSpeed onclick="return radCPUSpeed_onclick(1)" value="4.8 GHz"></td> <td>1 GHz</td> <td><input type="radio" name=radCPUSpeed onclick="return radCPUSpeed_onclick(2)" value="6 GHz"></td> <td>1.5 GHz</td> </tr> </table>
The radio button group name is radCPUSpeed. I've set the first one to be checked by default by including the word CHECKED inside the <input> tag's definition. It's a good idea to ensure that you have one radio button checked by default because if the user did not select a button, the form would be submitted with no value for that radio group.
We're making use of the onclick event of each Radio object. For each button we're connecting to the same function, radCPUSpeed_onclick(), but for each radio button, we are passing a value—the index of that particular button in the radCPUSpeed radio button group array. This makes it easy to determine which radio button was selected. We'll look at this function a little later, but first let's look at the standard button that completes our form.
<input type="button" value="Check Form" name=butCheck onclick="return butCheck_onclick()">
This button's onclick event handler is connected to the butCheck_onclick() function and is for the user to click when she has completed filling in the form.
So, we have two functions, radCPUSpeed_onclick() and butCheck_onclick(). These are both defined in the script block in the head of the page. Let's look at this script block now. It starts by declaring a variable radCpuSpeedIndex. This will be used to store the currently selected index of the radCPUSpeed radio button group.
var radCpuSpeedIndex = 0;
Next we have the radCPUSpeed_onclick() function, which is called by the onclick event handler in each radio button. Our function has one parameter, namely the index position in the radCPUSpeed[] array of the radio object selected.
function radCPUSpeed_onclick(radIndex) { var returnValue = true;
The first thing we do in the function is declare the returnValue variable and set it to true. We'll be returning this as our return value from the function. In this case the return value is important because it decides whether the radio button remains checked as a result of the user clicking it. If we return false, that cancels the user's action, and the radio button remains unchecked. In fact no radio button becomes checked, which is why we keep track of the index of the checked radio button so we can track which button was the previously checked one. To allow the user's action to proceed, we return true.
As an example of this in action, we have an if statement on the next line. If the radio button's index value passed is 1 (the user checked the box for a 4.8-GHz processor), we tell the user that it's out of stock and cancel the clicking action by setting returnValue to false.
if (radIndex == 1) { returnValue = false; alert("Sorry that processor speed is currently unavailable"); document.form1.radCPUSpeed[radCpuSpeedIndex].checked = true; }
I mentioned that canceling the clicking action results in no radio buttons being checked, so to rectify this, we set the previously checked box to be checked again in the following line:
document.form1.radCPUSpeed[radCpuSpeedIndex].checked = true;
What we are doing here is using the Array object for the radCpuSpeed radio group. Each array element actually contains an object, namely each of our three Radio objects. We use the radCpuSpeedIndex variable as the index of the Radio object that was last checked, since this is what it holds.
Finally, in the else statement we set radCpuSpeedIndex to the new checked radio button's index value.
else { radCpuSpeedIndex = radIndex; }
In the last line of the function, the value of returnValue is returned to where the function was called and will either cancel or allow the clicking action.
return returnValue; }
Our second function, butCheck_onclick(), is the one connected to the button's onclick event. In a real e-commerce situation, this button would be the place where we'd check our form and then submit it to the server for processing. Here we use the form to show a message box confirming which boxes you have checked, as if you didn't already know!
At the top we declare the four local variables, which will be used in the function. The variable numberOfControls is set to the form's length property, which is the number of elements on the form. The variable compSpec is used to build up the string that we'll display in a message box.
function butCheck_onclick() { var controlIndex; var element; var numberOfControls = document.form1.length; var compSpec = "Your chosen processor speed is "; compSpec = compSpec + document.form1.radCPUSpeed[radCpuSpeedIndex].value; compSpec = compSpec + "\nWith the following additional components\n";
In the following line:
compSpec = compSpec + document.form1.radCPUSpeed[radCpuSpeedIndex].value;
we add the value of the radio button the user has selected to our message string. The global variable radCpuSpeedIndex, which was set by the radio button group's onclick event, contains the array index of the selected radio button.
An alternative way of finding out which radio button was clicked would be to loop through the radio button group's array and test each radio button in turn to see if it was checked. The code would look something like this:
var radIndex; for (radIndex = 0; radIndex < document.form1.radCPUSpeed.length; radIndex++) { if (document.form1.radCPUSpeed[radIndex].checked == true) { radCpuSpeedIndex = radIndex; break; } }
Back to the actual code, you'll notice I've thrown in a few new line (\n) characters into the message string for formatting reasons.
Next we have our big for statement.
for (controlIndex = 0; controlIndex < numberOfControls; controlIndex++) { element = document.form1[controlIndex]; if (element.type == "checkbox") { if (element.checked == true) { compSpec = compSpec + element.value + "\n"; } } } alert(compSpec); }
It's here that we loop through each element on the form using document.form1[controlIndex], which returns a reference to the element object stored at the controlIndex index position.
You'll see that I've set the element variable to reference the object stored in the form1[] array at the index position stored in variable controlIndex. Again this is for convenient shorthand purposes; now to use that particular object's properties or methods, we just type element, a period, and then the method or property name, making our code easier to read and debug, which also saves on typing.
We only want to see which checkboxes have been checked, so we use the type property, which every HTML element object has, to see what element type we are dealing with. If the type is checkbox, we go ahead and see if it's a checked checkbox. If so, we append its value to the message string in compSpec. If it is not a checkbox, it can be safely ignored.
Finally, we use the alert() method to display the contents of our message string.
Although they look quite different, the drop-down list and the list boxes are actually both elements created with the <select> tag, and strictly speaking they are both select elements. The select element has one or more options in a list that you can select from; each of these options is defined using the <option> tag. Your list of <option> tags goes in between the <select> and </select> tags.
The size attribute of the <select> tag is used to specify how many of the options are visible to the user.
For example, to create a list box that is five rows deep and populate it with seven options, our <select> tag would look like this:
<select name=theDay size=5> <option value=0 selected>Monday <option value=1>Tuesday <option value=2>Wednesday <option value=3>Thursday <option value=4>Friday <option value=5>Saturday <option value=6>Sunday </select>
Notice that the Monday <option> tag also contains the word selected; this will make this option the default selected one when the page is loaded. The values of the options have been defined as numbers, but text would be equally valid.
If we want this to be a drop-down list, we just need to change the size attribute in the <select> tag to 1, and presto, it's a drop-down list.
If we want to let the user choose more than one item from a list at once, we simply need to add the multiple attribute to the <select> definition.
The <select> tag creates a Select object. This object has an options[] array property, and this array is made up of Option objects, one for each <option> element inside the <select> element associated with the Select object. For instance, in the preceding example, if the <select> element was contained in a form called theForm, with the following:
document.theForm.theDay.options[0]
we would access the option created for Monday.
How can we tell which option has been selected by the user? Easy: we use the Select object's selectedIndex property. We can use the index value returned by this property to access the selected option using the options[] array.
The Option object also has index, text, and value properties. The index property returns the index position of that option in the options[] array. The text property is what's displayed in the list, and the value property is the value defined for the option, which would be posted to the server if the form were submitted.
If you want to find out how many options there are in a select element, you can use the length property of either the Select object itself or of its options[] array property.
Let's see how we could loop through the options[] array for the preceding select box:
var theDayElement = window.document.form1.theDay; document.write("There are " + theDayElement.length + "options<br>"); var optionCounter; for (optionCounter = 0; optionCounter < theDayElement.length; optionCounter++) { document.write("Option text is " + theDayElement.options[optionCounter].text) document.write(" and its value is "); document.write(theDayElement.options[optionCounter].value); document.write("<br>") }
First we set the variable theDayElement to reference the Select object. Then we write the number of options to the page, in this case 7.
Next we use a for loop to loop through the options[] array, displaying the text of each option, such as Monday, Tuesday, and so on, and its value, such as 0, 1, and so on. If you create a page based on this code, it must be placed after the <select> tag has been defined.
It's also possible to add options to a select element after the page has finished loading. We'll look at how this is done next.
To add a new option to a select element, we simply create a new Option object using the new operator and then insert it into the options[] array of the Select object at an empty index position.
When creating a new Option object, there are two parameters to pass—the first is the text you want to appear in the list, and the second the value to be assigned to the option.
var myNewOption = new Option("TheText","TheValue");
We then simply assign this Option object to an empty array element, for example:
document.theForm.theSelectObject.options[0] = myNewOption;
If you want to remove an option, you simply set that part of the options[] array to null. For example, to remove the element we just inserted, we need the following:
document.theForm.theSelectObject.options[0] = null;
When you remove an Option object from the options[] array, the array is reordered so that the array index values of all the options above the removed one have their index value decremented by one.
When you insert a new option at a certain index position, beware that it will overwrite any Option object that is already there.
Let's use the "list of days" example we saw previously to demonstrate adding and removing list options.
<html> <head> <script language=JavaScript> function butRemoveWed_onclick() { if (document.form1.theDay.options[2].text == "Wednesday") { document.form1.theDay.options[2] = null; } else { alert('There is no Wednesday here!'); } } function butAddWed_onclick() { if (document.form1.theDay.options[2].text != "Wednesday") { var indexCounter; var days = document.form1.theDay; var lastoption = new Option(); days.options[6] = lastoption; for (indexCounter = 6;indexCounter > 2; indexCounter--) { days.options[indexCounter].text = days.options[indexCounter - 1].text; days.options[indexCounter].value = days.options[indexCounter - 1].value; } var option = new Option("Wednesday",2); days.options[2] = option; } else { alert('Do you want to have TWO Wednesdays?????'); } } </script> </head> <body> <form name=form1> <select name=theDay size=5> <option value=0 selected>Monday <option value=1>Tuesday <option value=2>Wednesday <option value=3>Thursday <option value=4>Friday <option value=5>Saturday <option value=6>Sunday </select> <br> <input type="button" value="Remove Wednesday" name=butRemoveWed onclick="butRemoveWed_onclick()"> <input type="button" value="Add Wednesday" name=butAddWed onclick="butAddWed_onclick()"> <br> </form> </body> </html>
Save this as ch6_examp7.htm. If you type the page in and load it into your browser, you should see the form shown in Figure 6-9. Click the Remove Wednesday button, and you'll see it disappear from the list. Add it back by clicking the Add Wednesday button. If you try and add a second Wednesday or remove a nonexistent Wednesday, you'll get a polite warning telling you that you can't do that.
Within the body of the page, we define a form with the name form1. This contains the select element, which includes day-of-the-week options that we have seen previously. The form also contains two buttons, as shown in the following:
<input type="button" value="Remove Wednesday" name=butRemoveWed onclick="butRemoveWed_onclick()"> <input type="button" value="Add Wednesday" name=butAddWed onclick="butAddWed_onclick()">
Each of these buttons has its onclick event handler connected to some code that calls one of two functions: butRemoveWed_onclick() and butAddWed_onclick(). These functions are defined in a script block in the head of the page. We'll take a look at each of them in turn.
At the top of the page we have our first function, butRemoveWed_onclick(), which removes the Wednesday option.
function butRemoveWed_onclick() { if (document.form1.theDay.options[2].text == "Wednesday") { document.form1.theDay.options[2] = null; } else { alert('There is no Wednesday here!'); } }
The first thing we do in the function is a sanity check: we must only try to remove the Wednesday option—if it's there in the first place! We do this by seeing if the third option in the array (with index 2 because arrays start at index 0) has the text "Wednesday". If it does we can remove the Wednesday option by setting that particular option to null. If the third option in the array is not Wednesday, we alert the user to the fact that there is no Wednesday to remove. Although I've used the text property in the if statement's condition, we could just have easily used the value property; it makes no difference.
Next we come to the butAddWed_onclick() function, which, as the name suggests, adds the Wednesday option.
This is slightly more complex than the code required to remove an option. First we use an if statement to check that there is not already a Wednesday option.
function butAddWed_onclick() { if (document.form1.theDay.options[2].text != "Wednesday") { var indexCounter; var days = document.form1.theDay; var lastoption = new Option(); days.options[6] = lastoption; for (indexCounter = 6;indexCounter > 2; indexCounter--) { days.options[indexCounter].text = days.options[indexCounter - 1].text; days.options[indexCounter].value = days.options[indexCounter - 1].value; }
If there is no Wednesday option, we then need to make space for the new Wednesday option to be inserted.
Before we do this, we define two variables, indexCounter and days, which refers to theDay select element and is a shorthand reference for our convenience. Next we create a new option with the variable name lastoption and assign this new option to the element at index position 6 in our options array. This previously had no contents. We next assign the text and value properties of all the Option objects from Thursday to Sunday to the Option that is at one higher index in the options array, leaving a space in the options array at position 2 to put Wednesday in. This is the task for the for loop within the if statement.
Next, we create a new Option object by passing the text "Wednesday" and the value 2 to the Option constructor. The Option object is then inserted into the options[] array at position 2, and presto, it appears in our select box.
var option = new Option("Wednesday",2); days.options[2] = option; }
We end the function by alerting the user to the fact that there is already a Wednesday option in the list, if the condition in the if statement is false.
else { alert('Do you want to have TWO Wednesdays?????'); } }
In IE, many more additional properties, methods, and events are associated with objects. In particular, the options[] array we are interested in has the additional add() and remove() methods, which add and remove options. These make life a little simpler.
Before we add an option, we need to create it. This is done in exactly the same way as before, using the new operator.
The add() method allows us to insert an Option object that we have created and takes two parameters. We pass the option that we want to add as the first parameter. The optional second parameter allows us to specify which index position we want to add the option in. This won't overwrite any Option object already at that position, but simply moves the Option objects up the array to make space. This is basically the same as what we had to code into the butAddWed_onclick() function using our for loop.
Using the add() method, we can rewrite the butAddWed_onclick() function in our ch6_examp7.htm example to look like this:
function butAddWed_onclick() { if (document.form1.theDay.options[2].text != "Wednesday") { var option = new Option("Wednesday",2); document.form1.theDay.options.add(option,2); } else { alert('Do you want to have TWO Wednesdays?????'); } }
The remove() method takes just one parameter, namely the index of the option we want removed. When an option is removed, the options at higher index positions are moved down the array to fill the gap.
Using the remove() method, we can rewrite the butRemoveWed_onclick() function in our ch6_examp7.htm example to look like this:
function butRemoveWed_onclick()
{
if (document.form1.theDay.options[2].text == "Wednesday")
{
document.form1.theDay.options.remove(2);
}
else
{
alert('There is no Wednesday here!');
}
}
Modify the previous example and save it as ch6_examp8_IE.htm, before loading it into IE. You'll see that it works just as the previous version did.
Select elements have three event handlers, onblur, onfocus, and onchange. We've seen all these events before. We saw the onchange event with the text box element, where it fired when focus was moved away from the text box and the value in the text box had changed. Here it fires when the user changes which option in the list is selected.
Let's take a look at an example that uses the onchange event and makes good use of the select element in its drop-down list form. Its purpose is to calculate the difference, in days, between two dates as set by the user via drop-down list boxes.
<html> <head> <script language=JavaScript> function writeOptions(startNumber, endNumber) { var optionCounter; for (optionCounter = startNumber; optionCounter <= endNumber; optionCounter++) { document.write('<option value=' + optionCounter + '>' + optionCounter); } } function writeMonthOptions() { var theMonth; var monthCounter; var theDate = new Date(1); for (monthCounter = 0; monthCounter < 12; monthCounter++) { theDate.setMonth(monthCounter); theMonth = theDate.toString(); theMonth = theMonth.substr(4,3); document.write('<option value=' + theMonth + '>' + theMonth); } } function recalcDateDiff() { var myForm = document.form1; var firstDay = myForm.firstDay.options[myForm.firstDay.selectedIndex].value; var secondDay = myForm.secondDay.options[myForm.secondDay.selectedIndex].value; var firstMonth = myForm.firstMonth.options[myForm.firstMonth.selectedIndex].value; var secondMonth = myForm.secondMonth.options[myForm.secondMonth.selectedIndex].value; var firstYear = myForm.firstYear.options[myForm.firstYear.selectedIndex].value; var secondYear = myForm.secondYear.options[myForm.secondYear.selectedIndex].value; var firstDate = new Date(firstDay + " " + firstMonth + " " + firstYear); var secondDate = new Date(secondDay + " " + secondMonth + " " + secondYear); var daysDiff = (secondDate.valueOf() - firstDate.valueOf()); daysDiff = Math.floor(Math.abs((((daysDiff / 1000) / 60) / 60) / 24)); myForm.txtDays.value = daysDiff; return true; } function window_onload() { var theForm = document.form1; var nowDate = new Date(); theForm.firstDay.options[nowDate.getDate() - 1].selected = true; theForm.secondDay.options[nowDate.getDate() - 1].selected = true; theForm.firstMonth.options[nowDate.getMonth()].selected = true; theForm.secondMonth.options[nowDate.getMonth()].selected = true; theForm.firstYear.options[nowDate.getFullYear()- 1970].selected = true; theForm.secondYear.options[nowDate.getFullYear() - 1970].selected = true; } </script> </head> <body language=JavaScript onload="return window_onload()"> <form name=form1> <p> First Date<br> <select name=firstDay size=1 onchange="return recalcDateDiff()"> <script language=JavaScript> writeOptions(1,31); </script> </select> <select name=firstMonth size=1 onchange="return recalcDateDiff()"> <script language=JavaScript> writeMonthOptions(); </script> </select> <select name=firstYear size=1 onchange="return recalcDateDiff()"> <script language=JavaScript> writeOptions(1970,2010); </script> </select> </p> <p> Second Date<br> <select name=secondDay size=1 onchange="return recalcDateDiff()"> <script language=JavaScript> writeOptions(1,31); </script> </select> <select name=secondMonth size=1 onchange="return recalcDateDiff()"> <script language=JavaScript> writeMonthOptions(); </script> </select> <select name=secondYear size=1 onchange="return recalcDateDiff()"> <script language=JavaScript> writeOptions(1970,2010); </script> </select> </p> Total difference in days <input type="text" name=txtDays value=0 readonly> <br> </form> </body> </html>
Call the example ch6_examp9.htm and load it into your web browser. You should see the form shown in Figure 6-10, but with both date boxes set to the current date.
If you change any of the select boxes, the difference between the days will be recalculated and shown in the text box.
In the body of the page, the form in the web page is built up using six drop-down list boxes and one text box. Let's look at an example of one of these select elements; take the first <select> tag, the one that allows the user to choose the day part of the first date.
<select name=firstDay size=1 onchange="return recalcDateDiff()"> <script language=JavaScript> writeOptions(1,31); </script> </select>
The size attribute has been set to 1 so that we have a drop-down list box rather than a list box, though strictly speaking 1 is the default, so we don't have to specify it. The onchange event handler has been connected to the recalcDateDiff() function that we'll be looking at shortly.
However, there are no <option> tags defined within the <select> element. With all the options that the drop-down list boxes need to be populated with, I'd have to be one cruel person to make you type it all in! Instead we populate the options using the functions, which make use of the document.write() method.
The date and year options are populated using the writeOptions() function declared in the head of the page. The function is passed two values, the start number and the end number of the options that we want the select element to be populated with. Let's look at the writeOptions() function.
function writeOptions(startNumber, endNumber) { var optionCounter; for (optionCounter = startNumber; optionCounter <= endNumber; optionCounter++) { document.write('<option value=' + optionCounter + '>' + optionCounter); } }
The function is actually quite simple, consisting of a for loop that loops from the first number (startNumber) through to the last (endNumber) using the variable optionCounter, and writes out the HTML necessary for each <option> tag. The text for the option and the value attribute of the <option> tag are specified to be the value of the variable optionCounter. It's certainly a lot quicker than typing out the 31 <option> tags necessary for the dates in a month.
You might wonder why the document.write() method is being used at all; why not use the methods of adding new options we saw before?
First, this way works with both IE and NN, so there is no need to write two separate bits of code. Second, adding new options to <select> elements when the page is being loaded sometimes proves difficult in Netscape 4 because a page refresh may be required before the changes take effect.
For the year select box the same function can be re-used. We just pass 1970 and 2010 as parameters to the writeOptions() function to populate the year select box.
<select name=firstYear size=1 onchange="return recalcDateDiff()"> <script language=JavaScript> writeOptions(1970,2010); </script> </select>
To populate the month select box with the names of each month, a different function will be needed. However, the principle behind populating the <select> element remains the same, using document.write(). The function in this case is writeMonthOptions() as you can see from the following month select element.
<select name=firstMonth size=1 onchange="return recalcDateDiff()"> <script language=JavaScript> writeMonthOptions(); </script> </select>
The new function, writeMonthOptions(), is defined in the head of the page. Let's take a look at it now. We start the function by defining three variables and initializing the variable, theDate, to the first day of the current month.
function writeMonthOptions() { var theMonth; var monthCounter; var theDate = new Date(1);
We use the Date object we have stored to get the months as text (Jan, Feb, . . . ,Dec). We get these months by setting the month in the theDate variable from 0 up to 11 using the setMonth() method in a for loop. Although the Date object does not provide a method for returning the date as anything other than a number, it does have the toString() method, which returns the value, as a string, of the date stored in the variable. It returns it in the format of day of week, month, day of the month, time, and finally year, for example Sat Feb 19 19:04:34 2000. We just need the month part. Since we always know where it will be in the string and that its length is always 3, we can easily use the String object's substr() method to extract the month. One thing to note is that while this example works fine with current versions of IE and Netscape, other browsers such as Opera actually return a different format and so this code won't work with those.
for (monthCounter = 0; monthCounter < 12; monthCounter++) { theDate.setMonth(monthCounter); theMonth = theDate.toString(); theMonth = theMonth.substr(4,3); document.write('<option value=' + theMonth + '>' + theMonth); } }
Now that we have our month as a string of three characters, we can create the <option> tag and populate its text and value with the month.
For user convenience during the loading of the page, it would be nice to set both of the dates in the select elements to today's date. This is what we do in the window_onload() function, which is connected to the window's onload event using the <body> tag.
<body language=JavaScript onload="return window_onload()">
The window_onload() function is defined in the head of the page. We start the function by setting the theForm variable to reference our Form object, because it shortens the reference needed in our code. Next we create a variable to hold a Date object to store today's date.
function window_onload() { var theForm = document.form1; var nowDate = new Date();
Setting each of the <select> box's initial values is easy; the value returned by the Date object nowDate can be modified to provide the required index of the options[] array. For the day, the correct index is simply the day of the month minus 1—remember that arrays start at zero, so day 1 is actually at index 0. The selected property is set to true to make that day the currently selected option in the list.
theForm.firstDay.options[nowDate.getDate() - 1].selected = true; theForm.secondDay.options[nowDate.getDate() - 1].selected = true;
The month is even easier because the getMonth() function returns a value from 0 to 11 for the month, which exactly matches the necessary index value for our options[] array.
theForm.firstMonth.options[nowDate.getMonth()].selected = true; theForm.secondMonth.options[nowDate.getMonth()].selected = true;
For the year, because we are starting with 1970 as our first year, we need to take 1970 from the current year to get the correct index value.
theForm.firstYear.options[nowDate.getFullYear() - 1970].selected = true; theForm.secondYear.options[nowDate.getFullYear() - 1970].selected = true; }
The final part of our code we need to look at is the function connected to the onchange event of each select element, namely the recalcDateDiff() function. Our first task in this function is to build up the two dates the user has selected using the drop-down lists.
function recalcDateDiff() { var myForm = document.form1; var firstDay = myForm.firstDay.options[myForm.firstDay.selectedIndex].value; var secondDay = myForm.secondDay.options[myForm.secondDay.selectedIndex].value; var firstMonth = myForm.firstMonth.options[myForm.firstMonth.selectedIndex].value; var secondMonth = myForm.secondMonth.options[myForm.secondMonth.selectedIndex].value; var firstYear = myForm.firstYear.options[myForm.firstYear.selectedIndex].value; var secondYear = myForm.secondYear.options[myForm.secondYear.selectedIndex].value;
We go through each select element and retrieve the value of the selected Option object. The selectedIndex property of the Select object provides the index we need to reference the selected Option object in the options[] array. For example, in the following line:
var firstDay = myForm.firstDay.options[myForm.firstDay.selectedIndex].value;
the index is provided by myForm.firstDay.selectedIndex. We then use that value inside the square brackets as the index value for the options[] array of the firstDay select element. This provides the reference to the selected Option object, whose value property we store in the variable firstDay.
We use this technique for all the remaining select elements.
We can then create new Date objects based on the values obtained from the select elements and store them in the variables firstDate and secondDate.
var firstDate = new Date(firstDay + " " + firstMonth + " " + firstYear); var secondDate = new Date(secondDay + " " + secondMonth + " " + secondYear);
Finally, we need to calculate the difference in days between the two dates.
var daysDiff = (secondDate.valueOf() - firstDate.valueOf()); daysDiff = Math.floor(Math.abs((((daysDiff / 1000) / 60) / 60) / 24));
The Date object has a method, valueOf(), which returns the number of milliseconds from 1st Jan 1970 to the date stored in the Date object. We subtract the value of the valueOf property of firstDate from the value of the valueOf property of secondDate and store this in the variable daysDiff. At this point, it holds the difference between the two dates in milliseconds, so we convert this to days in the following line. By dividing by 1,000 we make the value seconds, dividing by 60 makes this minutes, by 60 again makes it hours, and finally we divide by 24 to convert to our final figure of days difference. The Math object's abs() method makes negative numbers positive. The user may have set the first date to a later date than the second, and since we just want to find the number of days difference, not find out which is the earlier day, we make any negative results positive. The Math.floor() method removes the fractional part of any result and returns just the integer part rounded down to the nearest whole number.
Finally we write the number of days difference to the txtDays text box in the page.
myForm.txtDays.value = daysDiff; return true; }
That completes our look at the more useful form elements available in web pages. The next section returns to the trivia quiz, where we can put our newfound knowledge to good use and actually create a working quiz page.