Although HTML pages can be difficult to develop, they are usually very simple to use. The number of things you can do with an HTML page is quite limited. For the most part you simply look at it-read its text, admire its graphics, and, perhaps, listen to the sounds it can play. For many, the Web experience consists of visiting a series of pages without interacting with them. The only interaction occurs when the user selects a link or clicks an imagemap.
HTML forms have gradually changed that model to increase the level of interaction. A form can have a variety of ways of accepting input, including text fields, buttons, checkboxes, and multiple choice selections. In this way, HTML forms are a lot like paper forms. The user fills in the form, perhaps to purchase some item, and then submits the form. This submission process also mimics real life. It is difficult to tell if the form
has been properly filled in, and the time taken for processing the form is often quite lengthy. In the case of HTML, this processing delay occurs because the contents of the form must be sent across the network to some URL, processed there, and then returned to the user. Even the slightest error causes the form to be rejected, so that the form entry must be repeated.
One of the primary goals of JavaScript is to localize most of this process and perform the form validation within the user's browser. It won't be possible to actually submit an order locally, but it will be possible to make sure that the form is properly filled out locally, and thereby avoid forcing the user to redo the form. JavaScript realizes this goal through event handlers. Event handlers are JavaScript statements (usually functions) that are called whenever something happens. JavaScript functions can be called when a form is submitted, or they can be called whenever the user does anything to the form. If your form requires that a certain field correspond to a number between 2 and 10, for example, you can write a JavaScript function that will validate that field when the user changes it, and complain if the value is out of range.
This chapter describes event handling in JavaScript. We discuss all the events that can be handled, as well as the contexts in which these events may arise. In addition, you learn how JavaScript can be included within Web pages, and how JavaScript functions are connected with different components of that page.
To understand JavaScript's event handling model, you must first think about the things that can actually happen on a Web page. Although there are many different things you can do with the browser, most of these have nothing to do with Web navigation. When you save a page as text, print a page, or edit your hotlist, you are not actually navigating the Web. In these cases, you are using some of the graphical capabilities of the browser, which are independent of the Web.
To understand which browser actions correspond to JavaScript events and which do not, it is important to distinguish those actions that actually cause (or might cause) some change in the Web page being displayed. From the user's standpoint the number of such actions is actually quite limited. In fact, there are really only two types of top level actions: the user can navigate, or the user can interact with an element of an HTML form. Navigation means to change from one Web page to another, or perhaps to open a completely new page in a new window. Interaction with the contents of an HTML form means changing one or more of the elements in such a form that can be changed, such as editable text fields.
In the navigation category, you can distinguish the following different actions:
In most of these cases the current page will be unloaded, which means it will no longer be visible in any browser window. In several of these cases a new page will be loaded, which means its contents will be displayed in a browser window, perhaps a new one created specifically to display this particular page. Anyone who has used the World Wide Web realizes that selecting a hypertext link may not successfully take you to another. The machine to which that link points may be down, or simply inaccessible. The link may even be dead, meaning that it does not point to a valid destination. Selecting a dead link often unloads the current page, but doesn't load a new page. Most browsers display a blank page or post an error message. You may or may not be left on the current page, depending on the type of error and the browser being used. A sample error alert from Netscape is shown in fig-ure 3.1.
Figure 3.1 : Attempting to access a non-existent URL results in an error message on some browsers.
These events, loading and unloading a page, are the two document level events that can be handled by JavaScript. This means that it is possible to write JavaScript code, contained within the HTML definition of a page, that will be executed whenever that page is loaded. You can also have code that is executed whenever that page is unloaded. The dead link example illustrates the important fact that loading and unloading are two separate, unrelated events. When you attempt to activate a dead link, the current page is unloaded, but nothing is loaded in its place. In order to return to the last valid Web page you must use one of your browser's navigation controls. For example, if you select Back in Netscape the last page you visited is reloaded.
There are two additional events that are vaguely related to navigation. These events are the following:
When you move the mouse over a hypertext link, a mouseover event is generated. This event is not associated with clicking the link, it is associated with being poised to click it. This event can be used to give the user feedback, such as changing the color of the link or flashing it. The final event is the statechange event. This event has been proposed by Microsoft as part of their planned implementation of an open scripting architecture that will include Visual Basic Script as well as JavaScript. The purpose of this event is to provide staged notifications when a time-consuming operation is taking place. For example, if a movie player plug-in is being loaded, the statechange event might be issued to indicate the plug-in is ready to accept some user interaction, but cannot yet display its movie.
We have discussed the events that arise if you are using the browser to navigate the Web. Of course, you can also interact with your browser through the elements of an HTML form. Every form element that permits input is associated with one or more JavaScript events. We can broadly characterize the possible components of an HTML form as follows:
Button Elements in Forms Buttons come in five varieties, as follows:
Simple buttons are defined using the HTML <INPUT TYPE="button">. Checkboxes define options, which are either off (not checked) or on (checked). These are created using an <INPUT TYPE="checkbox"> directive. Radio buttons use the <INPUT TYPE="radio"> directive, and permit the user to select exactly one of a set of choices. Submit buttons and reset buttons are very special. Submit buttons, created by <INPUT TYPE="submit">, are used to end input operations on a form. When the submit button is pressed the contents of the form are packaged and sent to the URL target specified in the ACTION attribute of the <FORM> definition. Reset buttons bring the form back to its initial state, wiping out any input the user has performed; they are specified as <INPUT TYPE="reset">. Figure 3.2 shows a simple HTML form with all five button types; it was generated by the HTML file allbut.htm.
Figure 3.2 : All five types of HTML buttons have corresponding
JavaScript events.
TIP |
Hidden fields on HTML forms do not generate JavaScript events. |
The one thing the five types of buttons have in common is that you click the button to achieve its effect. Because this is an extremely common action, the JavaScript event model defines click as one of its HTML form events. This event is generated by each of the five button types. In addition, when a form is actually submitted, a submit event is generated. The submit event is really owned by the form being submitted, and not the submit button that causes it.
Text Elements in Forms There are three types of text items possible within an HTML form, as follows:
Single line text fields are created with an <INPUT TYPE="text"> directive. Any text you type in a text field is displayed as you type it. This behavior is known as echoing the input. Single line text fields that are created using <INPUT TYPE="password"> do not echo their input. Multi-line text fields are created with the TEXTAREA tag, and are usually called textareas. An HTML form showing all three types of text elements is shown in figure 3.3, created from the file alltxt.htm on the CD-ROM. Interacting with text is more complex than interacting with a button. There are more things you can do with text. You can click in the text field, enter text, edit text, select text, and decide you are finished with the text and move on.
Figure 3.3 : HTML text elements generate several different JavaScript events.
What are the events JavaScript generates in response to these
various actions? JavaScript uses a text manipulation model which
will be familiar to anyone who has ever used a windowing system.
It defines four events that are associated with text fields and
textareas, but not passwords fields-change, select,
focus, and blur. The first two should be self-explanatory.
The change event is generated whenever any text is changed,
and the select event is generated whenever text is selected.
Selecting text is more than simply clicking in the editable text
field or textarea. It means actually highlighting a portion of
the text with the mouse.
NOTE |
Password fields do not generate JavaScript events. This was a conscious design decision to prevent malicious script code from diverting password text. |
focus and blur are a little more involved. A text field or textarea is said to have focus when it is currently accepting input typed at the keyboard. Clicking anywhere inside a text item is certain to give it focus, and simply moving the mouse over the text field may do so as well. blur is the opposite of focus. Blur occurs when the text item no longer has focus. This may happen because some other item now has the focus, or because the focus has simply been lost. You will notice that if you position the mouse over a graphic (other than an imagemap), you can type until your fingers are sore, but nothing happens. This is a case where nothing has focus.
Selection Elements in Forms Selection lists are defined by the SELECT tag; their options are enumerated using the OPTION tag. They operate almost the same as text items; they are capable of generating focus, blur, and change events. Paradoxically, selection lists do not generate select events. You might well wonder why four event types are needed for text and three for lists. This is clarified later in this chapter. Figure 3.4 summarizes the events understood by JavaScript and the HTML elements that generate them.
Figure 3.4 : JavaScript events model different types of user interaction with a Web page.
Anything not mentioned in the previous two sections should be considered an action, not a JavaScript event. Scrolling a window, reading newsgroups, or answering mail are certainly actions, but they are not events. Using the Back, Forward, or Home buttons on Netscape's toolbar are not really JavaScript events, but they ultimately result in JavaScript events being delivered to the current page, since they unload the current document. Creating a bookmark or hotlist entry is not even remotely related to JavaScript events, since that does not affect the current page at all. How does one distinguish actions that might possibly be events from those which are not? The rule is that if an action affects or changes the current page, it is associated with one or more JavaScript events.
It might be argued that scrolling or resizing a window affects the current page, and should therefore result in some kind of event. Those of you who have programmed any kind of windowing system know these are visibility, or window damage, events. JavaScript takes a more literal definition of a page. No matter how much or how little of a Web page is visible, it is still the same page. Even if you only read the cartoons in The New Yorker, the articles are still there, unchanged.
So far we have talked about JavaScript as a language, and we have talked a bit about HTML, but we have not talked about how JavaScript is used in HTML. Event handlers are the glue that link HTML elements with JavaScript code, but how is it done? This section addresses this question. The answer has two parts: how JavaScript is included or referenced in a Web page, and how event handlers are attached to HTML items.
In the most general sense, every Web page is constructed from HTML statements that divide the page into two parts: the <HEAD> and the <BODY>. The HTML directives within the context of the <HEAD> give information about the page, while those in the <BODY> make up the page itself. In most simple HTML documents the <HEAD> usually contains only the <TITLE>. It can also contain a BASE tag which specifies a pathname that should be used to resolve relative HREFs within the document, and one or more LINK tags, which indicate the relationship of this document to one or more other documents, such as the browser's home page.
The HEAD section of an HTML document also contains the JavaScript code for your event handlers. While it is not absolutely necessary for all JavaScript code to go with the <HEAD>...</HEAD> delimiters, it is an excellent idea because it ensures that all JavaScript code has been defined before any of the <BODY> of the document is seen. In particular, if the document has a handler for the load event, and that event was triggered before that code had been read, an error would result because the event handler function would be undefined.
Syntax of the SCRIPT Tag JavaScript code is introduced with the SCRIPT tag. Everything between this tag and the closing /SCRIPT tag is assumed to be some kind of client-side script code, such as JavaScript. The syntax for the SCRIPT tag is
<SCRIPT LANGUAGE="LangName" [SRC="URL"]>
The element LangName gives the language that is used in the subsequent script code; this should be JavaScript. Strictly speaking, the LANGUAGE attribute is not required; at present, Netscape Navigator is the only browser that is widely available and understands any scripting language. Of course, the language it understands is JavaScript. This will certainly change very quickly, so it is a good idea to always include the LANGUAGE attribute.
If the SRC attribute is specified then it should reference a URL containing code in the script language. For JavaScript, this should be a valid URL for a file containing the JavaScript code. The filename should have the suffix .js. If the SRC attribute is given then the <SCRIPT> can be immediately terminated by a </SCRIPT> directive. A <SCRIPT> block that loads JavaScript code from a filename click.js in a directory jscode relative to the document base would look like this:
<SCRIPT LANGUAGE="JavaScript" SRC="jscode/click.js"> </SCRIPT>
NOTE |
Netscape Navigator 2.0 does not yet support the SRC attribute. This feature has been promised for version 2.1. |
If the SRC attribute is not given then it is expected
that all the code between <SCRIPT> and </SCRIPT>
is the script source itself. In the glorious future, when the
overwhelming majority of browsers understand the SCRIPT
tag, or at least benignly ignore it, the JavaScript source may
be given literally. Until then it is recommended that source included
between <SCRIPT> and </SCRIPT> be
enclosed within the HTML comment delimiters <!--
and -->. A simple example showing a single JavaScript
function is shown in listing 3.1.
CAUTION |
Use the C-style comments // and /* */ inside JavaScript code. Never use HTML comments inside JavaScript. |
Listing 3.1 A JavaScript <SCRIPT> with
a Single Function
<SCRIPT LANGUAGE="JavaScript"> <!-- function dontclickme() { // an ominous button click alert("I told you not to click me"); return( false ); } <!-- end script --> </SCRIPT>
Use of HTML Comments The function in listing 3.1 does not do much; it merely uses the alert() function to pop up a warning dialog box with its argument as the message. Presumably this function is the click event handler for a button you don't want the user to press. The important thing to notice about this simple example is the paradoxical, but important, use of HTML comments. The entire script body is enclosed with a comment, and the comment close --> is also paired with a second, seemingly redundant, comment start <!-- on the last line. At present, you should structure your script according to the following rules:
You should use this magic incantation not because it makes sense,
but because it works. Note that JavaScript code referenced through
a SRC URL should also follow these rules, as if it had literally
been included in the <SCRIPT> block. Note also
that you may have both a JavaScript SRC URL, and literal JavaScript
between <SCRIPT> and </SCRIPT>.
In this case, the URL referenced by the SRC attribute
is read and processed before the literal JavaScript.
CAUTION |
HTML comments are one of the least conforming areas of HTML. Most browsers deviate a little from the HTML standards, and some deviate a lot. The preceding comment rules may change in the future, and may be implemented differently on different browsers. |
Processing <SCRIPT> Code There are two important aspects to JavaScript code defined by or within a SCRIPT block. The first important principle is that this JavaScript code is not executed-it is merely read and checked for syntax errors. When the browser sees the code shown in listing 3.1, it does not execute the dontclickme() function, it merely recognizes that this function is a JavaScript function, and saves the definition of that function for later use. This is precisely the opposite behavior of normal HTML. When you say <HR> in an HTML document, you get a horizontal rule. You don't get it immediately, but you do get it when the browser has finished laying out the page (assuming that there are no HTML errors, of course).
This is the way that most interpreted languages work, however. If you create a Sub in BASIC, a defun in lisp, or a proc in Tcl, it is not executed when it is read. Instead, the interpreter parses it, which means that it scans through the function looking for obvious syntax errors, such as unbalanced parentheses, and records the function's definition for later use. The function is only used when it is called. In JavaScript, functions can only be called by events.
Binding in JavaScript Another critically important aspect of JavaScript is that it carries out dynamic binding. Binding refers to the way in which names of things, such as variable names, function names, and object names, are associated with the things themselves. If you call the function dontclickme from listing 3.1 by saying dontclickme(), you are not actually referring to the function itself, you are referring to the name of the function. "The Song of the Volga Boatmen" is really the name of that song, it is not the song itself. If you want the sheet music, you go to you favorite music store and ask for it by name; most people do not go in and begin singing "Eh-eh uxhnyot ."
There are two general approaches to binding: static binding and dynamic binding. Many languages, particularly compiled languages like C, C++, and Java, often insist on static binding. This means they require that they be able to find all named references when a program is compiled. (Of course, with the advent of dynamically loaded libraries this rule is relaxed a bit.) JavaScript uses the more liberal form, dynamic binding. JavaScript only attempts to resolve names when they are used.
Dynamic binding has several consequences. In the first place,
if the function dontclickme() is never called, then it
can contain all but the most hideous syntax errors and they will
never be found. If dontclickme is the event handler for
a button, and no one ever presses the button, its problems are
never exposed. Even if dontclickme() is absolutely perfect,
but the event handler is erroneously declared to be a function
named noclickme(), this mismatch will not be detected
until someone finally chooses to press the button. JavaScript
will only then try to find a function named noclickme().
It will fail, and an error will result. Dynamic binding is often
called runtime binding or late binding because the
binding process only takes place when the JavaScript interpreter
attempts to run the code.
TIP |
Always check meticulously to ensure that the function, object, and variable names used in HTML match those in the JavaScript code. |
Dynamic binding has its advantages and disadvantages. Dynamic binding is used by many interpreters because it simplifies the language, and makes it very easy to add in new functions. Since there is no brooding and melancholy compiler to satisfy, it is possible to build up a complex JavaScript application incrementally. Even if you really need an event handler for every possible event, you can start out with one or two handlers, make them work, and then gradually add more complexity.
The disadvantage of dynamic binding should be clear from the previous discussion. There is very little error checking. When the JavaScript interpreter is read-ing all the code in the SRC URL, or processing the code between <SCRIPT> and </SCRIPT>, it is performing some checking but it is by no means performing an exhaustive analysis of the code. Errors, particularly mismatched names, are not found until the erroneous code is executed. To see a more complete example of this, look at the HTML page defined in listing 3.2.
Listing 3.2 ex32.htm An Illustration
of Dynamic Binding
<HTML> <HEAD> <TITLE>A Potentially Dangerous JavaScript Page</TITLE> <SCRIPT LANGUAGE="JavaScript"> <!-- function dontclickme() { // button click handler alert("I told you not to click me"); } <!-- end script --> </SCRIPT> </HEAD> <BODY> <FORM METHOD="POST" ACTION="mailto:me@myhost.com"> <INPUT TYPE="button" NAME="mycheck" VALUE="HA!" onClick="dontclickme()"> </FORM> </BODY> </HTML>
If you copy this code, found in ex32.htm in the js directory on the CD-ROM, into a local file, change the e-mail address in the form's ACTION to your own e-mail address, and then read that file into your browser, everything will be fine. Notice that the click event handler for the button is declared using the HTML attribute onClick="dontclickme()", which tells JavaScript that when this button is pressed the function dontclickme should be called. (The exact syntax for declaring event handlers is discussed in the next section.) If you now click that button you should see something like figure 3.5.
Figure 3.5 : Clicking an HTML button invokes a JavaScript event handler that displays an alert.
So far so good. The name of the event handler in the HTML statement that created the button matched the name of a JavaScript function in the SCRIPT block. Now try the following experiment. Change the handler declaration from
onClick="dontclickme()"
to
onClick="noclickme()"
and then read that file into your browser. You will notice that the initial appearance of the HTML page is exactly as before. No errors have been reported. If you attempt to click the button labeled HA!, your browser reports an error, and the alert dialog box shown in figure 3.5 does not appear. This is dynamic binding at work. JavaScript did not know that the function named noclickme did not correspond to any currently defined function until the user action forced it to try to find one. Technically, the function name noclickme is said to be unbound.
It might seem like dynamic binding is a great potential source of error, without providing many benefits as compensation. As you will see when we discuss objects in chapter 4, "JavaScript Objects," objects may be defined and even modified on-the-fly. Dynamic binding allows you to refer to things that do not yet exist, but that will exist when the event handler which uses them is actually called. Dynamic binding, like the loose typing provided by JavaScript's var, is a two-edged sword. It must be used with care, but is very powerful.
Let's summarize these two critical points about JavaScript parsing and execution, since they will dominate our thinking for several chapters to come:
The previous section demonstrated that JavaScript functions are only executed in response to events. We also know that events themselves only occur when some interaction with or change to the current HTML page occurs. There must be a way in which we can link events to JavaScript functions in HTML. In fact, we have already seen one such example of this in listing 3.2. The mechanism is known as the event handler declaration.
Event handler declarations look exactly like ordinary HTML attributes. Each attribute name begins with the word on and is followed by the event name, so that onClick is the attribute that would be used to declare an event handler for the click event. The full declaration of an event handler looks like
onEvent="javascriptcode"
Attribute names are not case-sensitive, following the usual HTML convention. It is good practice, however, to use the coding style shown in the preceding line of code, with on in lowercase and the event name with an initial capital. This helps to distinguish it from other attributes, which are often fully capitalized.
The value of the attribute is a set of JavaScript code. The code may be included literally (known as inline JavaScript), or it may reference a JavaScript function. We can completely remove the dontclickme() function of listing 3.2 and write the button statement as
<INPUT TYPE="button" NAME="mycheck" VALUE="HA!" onClick="alert('I told you not to click me');">
This has two disadvantages. First, it tends to lead to very long
HTML statements. There is very little you can accomplish in only
a few characters. If you have hundreds of characters between the
opening (<) and the closing (>) of an HTML
statement it will almost certainly be very hard to read, and,
if it is too long, may cause your browser to choke. It is also
not modular. As you add event handlers for different HTML elements,
you may well find that there is a lot of common code. Each of
the button handlers might use a variation on the same code. Such
common code should always be encapsulated in a JavaScript function,
rather than being repeated in several places.
TIP |
Declare all event handlers as JavaScript functions. Avoid inline JavaScript code. |
One thing to notice about this example is the fact that the value of the onClick attribute is a quoted string. This follows standard HTML convention. Therefore, to include a string within the value of the attribute we must alternate single quotes (') with double quotes ("). This follows the JavaScript standard for strings, as we learned in the "Implicit Data Types in JavaScript" section of chapter 2. If you modify the dontclickme function to accept a string argument then you must carefully use quotes when passing in literal strings. Listing 3.3 shows a modified version of dontclickme, called donteventme, and the HTML event handler declarations which reference it. It can be found in the file ex33.htm on the CD-ROM.
Listing 3.3 ex33.htm A JavaScript Function
Can Be Shared by Several Event Handlers
<HTML> <HEAD> <TITLE>An Uncooperative JavaScript Page</TITLE> <SCRIPT LANGUAGE="JavaScript"> <!-- function donteventme( str ) { // generic diffident handler alert("I told you not to " + str + " me"); } <!-- end script --> </SCRIPT> </HEAD> <BODY> <FORM METHOD="post" ACTION="mailto:me@myhost.com"> <BR>No<INPUT TYPE="checkbox" NAME="mycheck" VALUE="HA!" onClick="donteventme('click')"> <SELECT NAME="mysel" onChange="donteventme('change')"> <OPTION SELECTED>Nope</OPTION> <OPTION>Not Me</OPTION> <OPTION>No Way</OPTION> </SELECT> </FORM> </BODY> </HTML>
In this example, the function donteventme is called whenever the checkbox is checked or any selection is made on the selection list. The alert function within the donteventme constructs an uncooperative message based on the function's string argument str. Although this example accomplishes no useful work, it is a perfect template for a JavaScript page. In general, a JavaScript page has the following three components:
You now know how to declare event handlers in general. The next
section shows exactly which handlers can be associated with specific
HTML tags, and gives various examples of how these event handlers
are used.
CAUTION |
Netscape Navigator 2.0 has a bug that can prevent JavaScript event handler code from being triggered. This occurs most often if an IMG directive is given without corresponding WIDTH and HEIGHT attributes. Make certain that you include these attributes if you are using images together with JavaScript. |
JavaScript events occur at three levels-at the level of the entire Web document, at the level of an individual <FORM> within the document, and at the level of an element of a <FORM> within that document. At the same time, any particular element at any of these three levels may result in more than one event. For example, you have already seen that text items can generate up to four different events depending on how they are manipulated. In this section, we examine each level and see which handlers are appropriate for the HTML elements within that level. As you might suspect, most of the action is at the lowest level, within HTML forms.
The HTML BODY tag is the container that holds the descriptive content of an HTML page. Just as the material in the HEAD section is about the page, the material between <BODY> and </BODY> is the page. The BODY tag can contain two event handler declarations using the onLoad and onUnload attributes. A JavaScript page might have a BODY declaration that looks like
<BODY onLoad="loadfunc()" onUnload="unloadfunc()">
The onLoad="loadfunc()" attribute declares a JavaScript handler that will handle the load event. The load event is generated after the entire contents of the page, namely the HTML between <BODY> and </BODY>, has been read, but before it has been displayed. The onLoad event handler is an excellent place to perform any one time initialization. It can also be used to display a splash screen containing company, product, or copyright information. It can even launch a security dialog box which permits only authorized users, with an appropriate password or key, from completely loading the page.
The onUnload="unloadfunc()" attribute declares an event handler that is invoked whenever the page is unloaded. This happens when the user executes any action that brings up a new page in the same browser window. An unload event does not occur if a new page is opened in a new window. Even if a new page is not successfully loaded, the current page is still unloaded, and the unloadfunc is called in that case. An onUnload event handler can be used to ensure that there are no loose ends, and to perform any cleanup necessary. For example, if the user has filled out a form, but has failed to press the Submit button, the onUnload handler should inform the user of that fact. It could even submit the form itself based on the user's response. Note that both the onLoad and onUnload handlers are optional.
There is one final document level event handler, although it is
not associated with the BODY tag. Any HTML link can declare
an event handler for the mouseOver event, which occurs
when the user places the mouse over the HREF of that
link. This can be used to achieve a visual effect, or to perform
some special processing before the user actually tries to access
the link. Listing 3.4 shows a slightly fanciful example. This
code can be found in the file ex34.htm on the CD-ROM.
CAUTION |
In the current implementation of JavaScript, links have event handlers, but anchors do not. This means that you must catch navigation events by attaching event handlers to links. If any of your links point to anchors in the same document, the event must be handled at the link, not at the anchor. |
Listing 3.4 ex34.htm Using the mouseOver
Event to Mediate Access
<HTML> <HEAD> <TITLE>A Nationalist JavaScript Page</TITLE> <SCRIPT LANGUAGE="JavaScript"> <!- function warnthem( lnk ) { // mouseOver event handler var theirhost = lnk.hostname; // 2; get hostname of link var domain = "", lastdot = 0, len = 0; len = theirhost.length; // 4; string length of hostname lastdot = theirhost.lastIndexOf("."); // 5; find last dot domain = theirhost.substring(lastdot+1, len); // 6; last part of hostname if ( domain == "zz" ) { // 7; warn about country "zz" alert("Country zz only has 1200 baud modems"); } } <!- end script -> </SCRIPT> </HEAD> <BODY> <HR> Check out the new links to <A HREF="http://home.std.zz" onMouseOver="warnthem(this)">Zzland</A> and its neighbor <A HREF="http://home.xyzzy.xy" onMouseOver="warnthem(this)">XYville</A> <HR> </BODY> </HTML>
This HTML creates a page with two elements-links to the fictitious home pages of the countries Zzland and XYville, and sets up a mouseOver event handler for those links. Note that the event handler function warnthem is called with an argument this. The special keyword this is used to refer to the current object. When the warnthem function is called, its parameter lnk is filled in with the object that represents the link over which the mouse just moved.
Statement 2 extracts the hostname part of that object, which in this example could be either home.std.zz or home.xyzzy.xy, depending on where the mouse is located. The next three statements use some of the string object functions (see "String Content Methods," in chapter 4) to tear off the last part of this fully qualified hostname, namely zz or xy, and save it in the variable domain. This variable is then tested against zz in statement 7. If the test passes then an alert is put up to warn the user that the connection to the zz home page will take longer due to slow modems. Links can also have click event handlers, so this code can be modified not only to warn the user, but also to abort the connection, if necessary. The result of placing the mouse over the Zzland link is shown in figure 3.6.
Figure 3.6 : JavaScript event handlers can be used with any hypertext links.
The FORM tag is used to begin the definition of an HTML form. It includes attributes from the METHOD to be used in submitting the form, the ACTION to be taken, and may also include a single type of event handler attribute, the onSubmit attribute. The syntax for a FORM tag is the following:
<FORM NAME="formname" ... onSubmit="submithandler()">
TIP |
Put event handler attributes last on the attribute list of an HTML tag. This makes them easy to find and modify during debugging. |
The onSubmit handler is invoked when the form's contents
are about to be submitted. This is a top level action that applies
to the entire form. It is also possible to specify an onClick
action on the Submit button in a form, as you shall see later
in the section, "Button Click Events." The natural use
of an onSubmit handler is to validate the contents of
a form. The submission proceeds if the contents are valid, and
is canceled if they are not.
CAUTION |
If you return false in an onSubmit event handler using UNIX version 2.0 of Netscape Navigator, it does not cancel the submit. |
Listing 3.5 (file ex35.htm on the CD-ROM) shows a very simple form with a single element, an editable text field. The value of the field is supposed to be a number between 1 and 9. The submit handler function checkit is called when the form is submitted. It validates the user-entered quantity and acts accordingly.
Listing 3.5 ex34.htm Form Content Can
Be Validated Using an onSubmit Handler
<HTML> <HEAD> <TITLE>A Simple Form Validation Example</TITLE> <SCRIPT LANGUAGE="JavaScript"> <!-- function checkit() { // submit validation function var strval = document.myform.mytext.value; // 2; input text value var intval = parseInt(strval); // 3; convert to integer if ( 0 < intval && intval < 10 ) { // 4; input ok return( true ); // 5; allow submit } else { // 6; input bad - tell user alert("Input value " + strval + " is out of range"); return( false ); // 8; forbid submit } } <!-- end script --> </SCRIPT> </HEAD> <BODY> <HR> <FORM NAME="myform" METHOD="post" ACTION="mailto:me@myhost.com" onSubmit="checkit()"> <P>Enter a number between 1 and 9: <INPUT TYPE="text" NAME="mytext" VALUE="1" SIZE="10"></P> <BR><INPUT TYPE="submit"> </FORM> <HR> </BODY> </HTML>
It is worthwhile to examine this example in some detail, as it exposes a number of points that are more thoroughly discussed later in this chapter. Let us consider the HTML in the <BODY> first. The FORM statement creates a form named myform with a fictitious mailto: destination. It also contains an onSubmit attribute that specifies checkit() as the JavaScript function to call when the form is about to be submitted. Like all of our previous event handlers, this one takes no arguments. You will see very shortly that it is not only possible to pass in arguments, but it can also be very beneficial. For a document this simple, however, it is not necessary.
The first INPUT tag establishes an editable text field named mytext which can hold up to 10 characters, and which will be initialized to the string "1". The second INPUT tag puts a Submit button just below the input text field. Neither of these INPUT statements have any handler attributes, although they could. What happens next?
If the user types in any text, or does anything except press the Submit button, then nothing special happens. This example does not process any events other than the submit event, so changes in the text field or navigation actions do not result in any JavaScript code being executed. If the user does press the Submit button, then the myform form tries to submit itself. This triggers the submit action, which results in its event handler, checkit(), being called.
The checkit function does two somewhat obscure things. In statement 2, it sets the local variable strval equal to the value of document.myform.mytext.value. We know from the "Functions and Objects" section of chapter 2 that the right side of this expression must be an object reference-in fact, a reference to an object within an object within an object. It is reasonable and correct to assume that the myform subobject corresponds to the HTML form named myform within the current document, and that the mytext subobject corresponds to the HTML editable text field named mytext inside myform. This windy construct transfers the value of that text field into the local variable strval. In statement 3, an attempt is made to convert this string to an integer using the built-in function parseInt. The putative integer is stored in intval.
In statement 4, our validation test is performed. If the string in the text field did represent an integer between 1 and 9 inclusive then this if test passes and the checkit function returns true, in statement 5. This is a message from JavaScript to the browser that the submission may complete.
If the text field was out of range then the else pathway in statement 6 is taken. Note that parseInt returns 0 if its argument cannot be parsed as an integer. This means that if the user entered "five" in the text field rather than "5" the value of intval will be 0, and the else clause will be taken. Statement 7 puts up an alert dialog box telling the user that the value was out of range. It contains the string representation of the value. This is useful since the alert dialog box may be inadvertently positioned over the text input field. Finally, statement 8 returns false, indicating that the submit operation should not complete. The outcome of entering a value that is out of bounds is shown in figure 3.7.
Figure 3.7 : JavaScript submit handlers are often used to validate form input.
In this particular case it is important to give the mytext
text field an initial value of 1. This ensures that if the user
clicks the Submit button without altering that text field it will
have an acceptable value, and the form will be submitted. In many
cases, just the opposite is true. The whole point of a catalog
order form is to persuade the user to enter critical information,
such as his name and e-mail address. In this case, it's a good
idea to initialize the text field with a deliberately invalid
value, so that if the user hits Submit without typing anything
the form is not submitted. Chapters 6, "Interactive HTML
Objects," and 16, "Creative User Interaction,"
provide several more sophisticated examples of customized user
interaction using JavaScript.
NOTE |
Always give the user meaningful feedback on inappropriate input or other error conditions. Indicate why and where the error occurred, not just that an error occurred. Be brief, but specific. N |
Almost all form elements may have one or more event handlers.
The type of event handlers permitted on a given element depends
on the type of element itself. You have already seen the linkage
between events and HTML entities in figure 3.4. Broadly speaking,
buttons can generate click events, and text and select
items can generate focus, blur, select,
and change events. The one potentially confusing aspect
of this organization of events is that selection lists cannot
generate the
select event. This is because they have no editable text.
We will not consider all possible events in this chapter, only
a pithy subset.
There are two important exceptions to the rule that all form elements can have handlers. The first exception applies to hidden items, those with <INPUT TYPE="hidden">. Since they cannot be seen, they cannot be changed by the user, and therefore cannot generate events. The second exception applies to individual OPTION elements within a SELECT selection list. The SELECT tag itself may have attributes declaring focus, blur, and change handlers, but the OPTIONs may not gen-erate their own events. Any acquisition or loss of focus, and any change in the item(s) that have been selected applies to the whole list, not to an individual element.
Button Click Events All button types within an HTML form can have click event handlers by adding an onClick attribute to their <INPUT> declaration. Simple buttons with a TYPE attribute of "button", "reset", or "submit" merely signal that they have been pressed. (Recall that the act of submitting a form may also be caught using an onSubmit handler attached to the <FORM> declaration.) Checkboxes and radio buttons also have values. Checkboxes and individual radio buttons can be asked if they are on or off. A group of radio buttons can also be asked for the unique index of the button currently checked.
One very common problem in HTML forms design is the issue of conflicting options. Users are often presented with a variety of different choices, which may even be spread out over more than one HTML form. Some combinations of choices may be invalid or dubious. Unfortunately, in standard HTML there is no way to perform input validation of this kind without actually submitting the form and asking the ACTION URL if that particular combination is acceptable.
JavaScript event handlers are ideal for this kind of validation. As you learn in chapter 4, every HTML form element is also a JavaScript object. You have already seen some examples of this in listings 3.4 and 3.5. Listing 3.6 shows two radio buttons working together with a checkbox using a JavaScript onClick event handler. The code for this listing can be found in file ex36.htm on the CD-ROM. The initial appearance of this form is shown in figure 3.8.
Figure 3.8 : JavaScript onClick handlers can be used to exclude invalid user input.
Listing 3.6 ex36.htm Values of Different
Form Elements Can Be Accessed in JavaScript
<HTML> <HEAD> <TITLE>Two Choices Work as One</TITLE> <SCRIPT LANGUAGE="JavaScript"> <!-- function insok() { // make sure payment & ins choices are compatible var isgold = document.myform.payment[1].checked; // 2; gold checked var isins = document.myform.insurance.checked; // 3; insurance elected? var ok = null; // 5; if paying in gold without insurance then.. if ( isgold == true && isins != true ) { ok = confirm("Do you want insurance?"); // 6; ask for insurance if ( ok == true ) { // 7; yes, get insurance document.myform.insurance.checked = true; // 8; check it } } <!-- end script --> </SCRIPT> </HEAD> <BODY> <HR> <FORM NAME="myform" METHOD="POST" ACTION="mailto:me@myhost.com"> <STRONG>Payment Options</STRONG><BR> <HR> <INPUT TYPE="radio" NAME="payment" VALUE="1" CHECKED onClick="insok()"> Personal Check <INPUT TYPE="radio" NAME="payment" VALUE="2" onClick="insok()"> Gold Bullion <HR> <INPUT TYPE="checkbox" NAME="insurance" VALUE="Ins"> Insurance? </FORM> <HR> </BODY> </HTML>
The <BODY> of this page sets up a two-choice radio button named payment, and a checkbox named insurance. The first button is selected, and the checkbox starts off unchecked. The radio button group has the function insok as its click event handler. Whenever either of the buttons is clicked the insok function is called.
In statements 2 and 3 insok fetches the current value of the second radio button named payment. Note that payment actually denotes the entire group of buttons, not any single radio button, so that you must use the array reference payment[1] in order to refer to the second button (0 based indexing is used). That value is stored in the boolean variable isgold. The variable insok gets the state of the insurance checkbox, which is also true if it is checked and false if it is not. A compatibility test is now performed in statement 5. If the radio button group indicates payment in gold bullion, but the insurance button is not checked, then a confirmation dialog box is put up using the confirm() function in statement 6.
The confirmation dialog box has OK and Cancel buttons. If the
user presses OK, the function returns true; otherwise
it returns false. The return value is tested in statement
7. If it was true, then the user does want insurance,
and the method function value of the checked property of the
myform.insurance object is set to true. Without
worrying too much about what methods and properties really mean
just yet, it is easy to infer that this assignment statement has
the same effect as a click of the insurance button. That checkbox
is now checked.
TROUBLESHOOTING |
I modified the code shown in listing 3.6. I added another group of radio buttons to collect information about the user's income level, with its own event handler doinc(). I would like to force the insok() function to be called from the new event handler. Inside doinc() I have a statement myform.insurance.click(); This click() function is supposed to cause the insurance checkbox to be checked, but the doins() handler is never called. Why? JavaScript has many functions like click() that emulate user actions. These emulated actions do not generate events, however, so the corresponding event handler functions are never called. There is nothing mystical about the event handler function insok()-it is an ordinary JavaScript function that happens to be linked to an HTML event. If you want to call insok() in your doinc() event handler, just do the following: insok(); |
Text Edit and Selection Events HTML text <INPUT> fields with a TYPE attribute of "text" may declare event handlers for any combination of the four text events: focus, blur, change, and select. Multi-line text input items created with a TEXTAREA tag may also have these handlers. Selection lists created with <SELECT> can generate all these events except select.
The focus event is generated when the text item of a list element gets the input focus, usually as a result of a mouse click. Tabbing through form fields also moves the input focus. The blur event is generated when an item which had focus looses it. The change event is generated whenever something changes. In a text item this results when any new text is entered or existing text is deleted. In a selection list it happens whenever a new selection is made, even in a list that permits MULTIPLE selections. The select event is generated when the user selects some text, usually by click-and-drag or double-click operations with the mouse. The select event is almost always accompanied by a visual cue, usually by the selected text becoming highlighted or changing color.
These events can be used to obtain very fine control over the content of text or selection list items. The most common application is to use the change or blur events to ensure that a text field has an appropriate value. If you ask the user to enter her birthdate, for example, and provide separate fields for the month, day, and year, you will almost certainly want to make sure that the value of the day field is a number between 1 and 31. You might even go to greater lengths, and limit the day field's value based on the value of the month field. In any case, you want to avoid erroneous input such as "bleen." Text events can also be used to coordinate the values coming from multiple form elements, as we saw in listing 3.6.
Listing 3.7 (file ex37.htm on the CD-ROM) shows a linguistic application of the blur event for a TEXTAREA. The user is inspired to enter a sentence without a single instance of the letter e. If the user tries and fails he is chided for his lack of creativity. Note that the blur event handler is only called if the user makes an attempt, since blur is only generated when focus is lost. If the user never clicks or types in the TEXTAREA no blur event occurs. Parts II and IV of this book provide many more detailed examples of all the JavaScript events.
Listing 3.7 ex37.htm An Example of JavaScript's
Text Events
<HTML> <HEAD> <TITLE>A Literary Exercise</TITLE> <SCRIPT LANGUAGE="JavaScript"> <!- function hasE() { // complain if there is an e var thestr = document.myform.mytarea.value; // 2; get textarea value var uthestr = thestr.toLowerCase(); // 3; convert to lowercase if ( uthestr == "" ) { // 4; no entry return; // 5; just return } if ( uthestr.indexOf("e") >= 0 ) { // 7; found an 'e' alert("Alors! You've got an E in there!"); // 8; failed } else { if ( uthestr.length <= 20 ) { alert("Nice try, but too brief"); // 11; too short } else { alert("Congratulations!"); // 13; succeeded } } } <!- end script -> </SCRIPT> </HEAD> <BODY> <P>The novel <I>A Void</I> does not contain a single "e".<BR> Can you create a sentence without one?</P> <HR> <FORM NAME="myform" METHOD="POST" ACTION="mailto:me@myhost.com"> <TEXTAREA NAME="mytarea" ROWS="5" COLUMNS="80" onBlur="hasE()"> </TEXTAREA> </FORM> <HR> </BODY> </HTML>
The modulus operandi of this example should be becoming familiar to you now. If the user types or clicks in the textarea nothing happens. When he leaves the textarea and clicks elsewhere a blur event is generated and the handler function hasE invoked. This function gets the contents of the textarea into a local variable named thestr (in statement 2) and then uses one of the string functions to convert it to lowercase (statement 3). This saves a little time, as the function won't have to test for the presence of both e and E. The new lowercase string uthestr is tested against the empty string in statement 4. If there is no text the function returns without complaint.
If there is some text, but it has an e the user is reprimanded in statement 8. If there is no e but the text has less than 20 characters the user is encouraged to try a more ambitious work in statement 11. If the text is long enough and has no e then the user is praised in statement 13. Of course, there is nothing preventing the user from entering gibberish such as zzzzzzzzzzzzzzzzzzzzzzzzz and being congratulated anyway. Much more sophisticated checking would be necessary to ensure that the input was actually a sentence.