Section 17.1. Basic Event Handling


17.1. Basic Event Handling

In the code shown so far in this book, event handlers have been written as strings of JavaScript code that are used as the values of certain HTML attributes, such as onclick. Although this is the key to the original event model, there are a number of additional details, described in the following sections, that you should understand.

17.1.1. Events and Event Types

Different occurrences generate different types of events. When the user moves the mouse over a hyperlink, it causes a different type of event than when the user clicks the mouse on the hyperlink. Even the same occurrence can generate different types of events based on context: when the user clicks the mouse over a Submit button, for example, it generates a different event than when the user clicks the mouse over the Reset button of a form.

In the original event model, an event is an abstraction internal to the web browser, and JavaScript code cannot manipulate an event directly. When we speak of an event type in the original event model, what we really mean is the name of the event handler that is invoked in response to the event. In this model, event-handling code is specified using the attributes of HTML elements (and the corresponding properties of the associated JavaScript objects). Thus, if your application needs to know when the user moves the mouse over a specific hyperlink, you use the onmouseover attribute of the <a> tag that defines the hyperlink. If the application needs to know when the user clicks the Submit button, you use the onclick attribute of the <input> tag that defines the button or the onsubmit attribute of the <form> element that contains that button.

There are quite a few different event-handler attributes that you can use in the original event model. They are listed in Table 17-1, which also specifies when these event handlers are triggered and which HTML elements support the handler attributes.

Table 17-1. Event handlers and the HTML elements that support them

Handler

Triggered when

Supported by

onabort

Image loading interrupted.

<img>

onblur

Element loses input focus.

<button>, <input>, <label>, <select>, <textarea>, <body>

onchange

Selection in a <select> element or other form element loses focus, and its value has changed since it gained focus.

<input>, <select>, <textarea>

onclick

Mouse press and release; follows mouseup event. Return false to cancel default action (i.e., follow link, reset, submit).

Most elements

ondblclick

Double-click.

Most elements

onerror

Error when loading image.

<img>

onfocus

Element gains input focus.

<button>, <input>, <label>, <select>, <textarea>, <body>

onkeydown

Key pressed down. Return false to cancel.

Form elements and <body>

onkeypress

Key pressed; follows keydown. Return false to cancel.

Form elements and <body>

onkeyup

Key released; follows keypress.

Form elements and <body>

onload

Document load complete.

<body>, <frameset>, <img>

onmousedown

Mouse button pressed.

Most elements

onmousemove

Mouse moved.

Most elements

onmouseout

Mouse moves off element.

Most elements

onmouseover

Mouse moves over element.

Most elements

onmouseup

Mouse button released.

Most elements

onreset

Form reset requested. Return false to prevent reset.

<form<

onresize

Window size changes.

<body>, <frameset>

onselect

Text selected.

<input>, <textarea>

onsubmit

Form submission requested. Return false to prevent submission.

<form>

onunload

Document or frameset unloaded.

<body>, <frameset>


As client-side JavaScript programming has evolved, so has the event model it supports. With each new browser version, new event-handler attributes have been added. Finally, the HTML 4 specification codified a standard set of event handler attributes for HTML tags. The third column of Table 17-1 specifies which HTML elements support each event handler attribute. For mouse event handlers, column three specifies that the handler attribute is supported by "most elements." The HTML elements that do not support these event handlers are typically elements that belong in the <head> of a document or do not have a graphical representation of their own. The tags that do not support the nearly universal mouse event handler attributes are <applet>, <bdo>, <br>, <font>, <frame>, <frameset>, <head>, <html>, <iframe>, <isindex>, <meta>, and <style>.

17.1.1.1. Device-dependent and device-independent events

If you study the various event handler attributes in Table 17-1 closely, you can discern two broad categories of events. One category is raw events or input events. These are the events that are generated when the user moves or clicks the mouse or presses a key on the keyboard. These low-level events simply describe a user's gesture and have no other meaning. The second category of events is semantic events. These higher-level events have a more complex meaning and can typically occur only in specific contexts: when the browser has finished loading the document or when a form is about to be submitted, for example. A semantic event often occurs as a side effect of a lower-level event. For example, when the user clicks the mouse over a Submit button, three of the button's input handlers are triggered: onmousedown, onmouseup, and onclick. Then, as a result of this mouse click, the HTML form that contains the button generates an onsubmit event.

Another important distinction divides events into device-dependent events, which are tied specifically to the mouse or to the keyboard, and device-independent events, which can be triggered in more than one way. This distinction is particularly important for accessibility (see Section 13.7.) because some users may be able to use a mouse but not a keyboard, and others may be able to use a keyboard but not a mouse. Semantic events, such as onsubmit and onchange, are almost always device-independent events: all modern browsers allow users to manipulate HTML forms using the mouse or keyboard traversal. The events that have the word "mouse" or "key" in them are clearly device-dependent events. If you use them, you may want to use them in pairs so that you provide handlers for both a mouse gesture and a keyboard alternative. Note that the onclick event can be considered a device-independent event. It is not mouse-dependent because keyboard activation of form controls and hyperlinks also generates this event.

17.1.2. Event Handlers as Attributes

As shown in several examples in earlier chapters, event handlers are specified (in the original event model) as strings of JavaScript code used for the values of HTML attributes. So, for example, to execute JavaScript code when the user clicks a button, specify that code as the value of the onclick attribute of the <input> (or <button>) tag:

 <input type="button" value="Press Me" onclick="alert('thanks');"> 

The value of an event handler attribute is an arbitrary string of JavaScript code. If the handler consists of multiple JavaScript statements, the statements must be separated from each other by semicolons. For example:

 <input type="button" value="Click Here"        onclick="if (window.numclicks) numclicks++; else numclicks=1;                this.value='Click # ' + numclicks;"> 

When an event handler requires multiple statements, it is usually easier to define them in the body of a function and then use the HTML event handler attribute to invoke that function. For example, if you want to validate a user's form input before submitting the form, you can use the onsubmit attribute of the <form> tag.[*] Form validation typically requires several lines of code, at a minimum, so instead of cramming all this code into one long attribute value, it makes more sense to define a form-validation function and simply use the onclick attribute to invoke that function. For example, if you defined a function named validateForm( ) to perform validation, you could invoke it from an event handler like this:

[*] Chapter 18 covers HTML forms in detail and includes a form-validation example.

 <form action="processform.cgi" onsubmit="return validateForm( );"> 

Remember that HTML is case-insensitive, so you can capitalize event-handler attributes any way you choose. One common convention is to use mixed-case capitalization, with the initial "on" prefix in lowercase: onClick, onLoad, onMouseOut, and so on. In this book, I've chosen to use all lowercase for compatibility with XHTML, which is case-sensitive.

The JavaScript code in an event-handler attribute may contain a return statement, and the return value may have special meaning to the browser. This is discussed shortly. Also, note that the JavaScript code of an event handler runs in a different scope (see Chapter 4) than global JavaScript code. This, too, is discussed in more detail later in this section.

17.1.3. Event Handlers as Properties

As shown in Chapter 15, each HTML element in a document has a corresponding DOM element in the document tree, and the properties of this JavaScript object correspond to the attributes of the HTML element. This applies to event-handler attributes as well. So if an <input>a tag has an onclick attribute, the event handler it contains can be referred to with the onclick property of the form element object. (JavaScript is case-sensitive, so regardless of the capitalization used for the HTML attribute, the JavaScript property must be all lowercase.)

Since the value of an HTML event handler attribute is a string of JavaScript code, you might expect the value of the corresponding JavaScript property to be a string as well. This is not the case: when accessed through JavaScript, event-handler properties are functions. You can verify this with a simple example:

 <input type="button" value="Click Here" onclick="alert(typeof this.onclick);"> 

If you click the button, it displays a dialog box containing the word "function," not the word "string." (Note that in event handlers, the this keyword refers to the object on which the event occurred. I'll discuss the this keyword shortly.)

To assign an event handler to a document element using JavaScript, simply set the event-handler property to the desired function. For example, consider the following HTML form:

 <form name="f1"> <input name="b1" type="button" value="Press Me"> </form> 

The button in this form can be referred to as document.f1.b1, which means that an event handler can be assigned with a line of JavaScript like this one:

 document.f1.b1.onclick=function( ) { alert('Thanks!'); }; 

An event handler can also be assigned like this:

 function plead( ) { document.f1.b1.value += ", please!"; } document.f1.b1.onmouseover = plead; 

Pay particular attention to that last line: there are no parentheses after the name of the function. To define an event handler, you assign the function itselfnot the result of invoking the functionto the event handler property. This often trips up beginning JavaScript programmers.

There are a couple of advantages to expressing event handlers as JavaScript properties. First, and most importantly, it reduces the intermingling of HTML and JavaScript, promoting modularity and cleaner, more maintainable code. Second, it allows event handler functions to be dynamic. Unlike HTML attributes, which are a static part of the document, JavaScript properties can be changed at any time. In complex interactive programs, it can sometimes be useful to dynamically change the event handlers registered for HTML elements.

One minor disadvantage to defining event handlers in JavaScript is that it separates the handler from the element to which it belongs. If the user interacts with a document element before the document is fully loaded (and before all its scripts have executed), the event handlers for the document element may not yet be defined.

Example 17-1 shows how you can specify a single function to be the event handler for many document elements. The example is a simple function that defines an onclick event handler for every link in a document. The event handler asks for the user's confirmation before allowing the browser to follow the hyperlink on which the user has just clicked. The event-handler function returns false if the user does not confirm, which prevents the browser from following the link. Event-handler return values will be discussed shortly.

Example 17-1. One function, many event handlers

 // This function is suitable for use as an onclick event handler for <a> and // <area> elements. It uses the this keyword to refer to the document element // and may return false to prevent the browser from following the link. function confirmLink( ) {     return confirm("Do you really want to visit " + this.href + "?"); } // This function loops through all the hyperlinks in a document and assigns // the confirmLink function to each one as an event handler. Don't call it // before the document is parsed and the links are all defined. It is best // to call it from the onload event handler of a <body> tag. function confirmAllLinks( ) {     for(var i = 0; i < document.links.length; i++) {         document.links[i].onclick = confirmLink;     } } 

17.1.3.1. Explicitly invoking event handlers

Because the values of JavaScript event handler properties are functions, you can use JavaScript to invoke event handler functions directly. For example, if you use the onsubmit attribute of a <form> tag to define a form-validation function and you want to validate the form at some point before the user attempts to submit it, you can use the onsubmit property of the Form object to invoke the event handler function. The code might look like this:

 document.myform.onsubmit( ); 

Note, however, that invoking an event handler is not a way to simulate what happens when the event actually occurs. If you invoke the onclick method of a Link object, for example, it does not make the browser follow the link and load a new document. It merely executes whatever function you've defined as the value of that property. (To make the browser load a new document, set the location property of the Window object, as shown in Chapter 14.) The same is true of the onsubmit method of a Form object or the onclick method of a Submit object: invoking the method runs the event-handler function but does not cause the form to be submitted. (To actually submit the form, call the submit( ) method of the Form object.)

You might want to explicitly invoke an event-handler function if you want to use JavaScript to augment an event handler that is (or may be) already defined by HTML code. Suppose you want to take a special action when the user clicks a button, but you do not want to disrupt any onclick event handler that may have been defined in the HTML document itself. (This is one of the problems with the code in Example 17-1 : by adding a handler for each hyperlink, it overwrites any onclick handlers that were already defined for those hyperlinks.) You might accomplish this with code like the following:

 var b = document.myform.mybutton;  // This is the button we're interested in var oldHandler = b.onclick;        // Save the HTML event handler function newHandler( ) { /* My event-handling code goes here */ } // Now assign a new event handler that calls both the old and new handlers b.onclick = function() { oldHandler( ); newHandler( ); } 

17.1.4. Event Handler Return Values

In many cases, an event handler (whether specified by HTML attribute or JavaScript property) uses its return value to indicate the disposition of the event. For example, if you use the onsubmit event handler of a Form object to perform form validation and discover that the user has not filled in all the fields, you can return false from the handler to prevent the form from actually being submitted. You can ensure that an empty form is not submitted with code like this:

 <form action="search.cgi"       onsubmit="if (this.elements[0].value.length == 0) return false;"> <input type="text"> </form> 

Generally, if the web browser performs some kind of default action in response to an event, you can return false to prevent the browser from performing that action. In addition to onsubmit, other event handlers from which you can return false to prevent the default action include onclick, onkeydown, onkeypress, onmousedown, onmouseup, and onreset. The second column of Table 17-1 describes what happens when you return false.

There is one exception to the rule about returning false to cancel: when the user moves the mouse over a hyperlink, the browser's default action is to display the link's URL in the status line. To prevent this from happening, you return TRue from the onmouseover event handler. For example, you can try to display a message other than a URL with code like this:

 <a href="help.htm" onmouseover="window.status='Help!!'; return true;">Help</a> 

There is no good reason for this exception: it is this way simply because that is always the way it has been. As noted in Chapter 14, however, most modern browsers consider the ability to hide the destination of a link to be a security hole and have disabled it. Therefore, the single exception to the "return false to cancel" rule has become moot.

Note that event handlers are never required to explicitly return a value. If you don't return a value, the default behavior occurs.

17.1.5. Event Handlers and the this Keyword

Whether you define an event handler with an HTML attribute or with a JavaScript property, you are assigning a function to a property of a document element. In other words, you're defining a new method of the document element. When your event handler is invoked, it is invoked as a method of the element on which the event occurred, so the this keyword refers to that target element. This behavior is useful and unsurprising.

Be sure, however, that you understand the implications. Suppose you have an object o with a method mymethod. You might register an event handler like this:

 button.onclick= o.mymethod; 

This statement makes button.onclick refer to the same function that o.mymethod does. This function is now a method of both o and button. When the browser triggers this event handler, it invokes the function as a method of the button object, not as a method of o. The this keyword refers to the Button object, not to your object o. Do not make the mistake of thinking you can trick the browser into invoking an event handler as a method of some other object. If you want to do that, you must do it explicitly, like this:

 button.onclick = function( ) { o.mymethod( ); } 

17.1.6. Scope of Event Handlers

As discussed in Section 8.8., functions in JavaScript are lexically scoped. This means that they run in the scope in which they were defined, not in the scope from which they are called. When you define an event handler by setting the value of an HTML attribute to a string of JavaScript code, you are implicitly defining a JavaScript function. It is important to understand that the scope of an event-handler function defined in this way is not the same as the scope of other normally defined global JavaScript functions. This means that event handlers defined as HTML attributes execute in a different scope than other functions.[*]

[*] It is important to understand this, and while the discussion that follows is interesting, it is also dense. You may want to skip it on your first time through this chapter and come back to it later.

Recall from the discussion in Chapter 4 that the scope of a function is defined by a scope chain, or list of objects, that is searched, in turn, for variable definitions. When a variable x is looked up or resolved in a normal function, JavaScript first looks for a local variable or argument by checking the call object of the function for a property of that name. If no such property is found, JavaScript proceeds to the next object in the scope chain: the global object. It checks the properties of the global object to see if the variable is a global variable.

Event handlers defined as HTML attributes have a more complex scope chain than this. The head of the scope chain is the call object. Any arguments passed to the event handler are defined here (you'll see later in this chapter that in some advanced event models, event handlers are passed an argument), as are any local variables declared in the body of the event handler. The next object in an event handler's scope chain isn't the global object, however; it is the object that triggered the event handler. So, for example, suppose you use an <input> tag to define a Button object in an HTML form and then use the onclick attribute to define an event handler. If the code for the event handler uses a variable named form, that variable is resolved to the form property of the Button object. This can be a useful shortcut when writing event handlers as HTML attributes. For example:

 <form>   <!-- In event handlers, "this" refers to the target element of the event -->   <!-- So we can refer to a sibling element in the form like this -->   <input  type="button" value="Button 1"          onclick="alert(this.form.b2.value);">   <!-- The target element is also in the scope chain, so we can omit "this" -->   <input  type="button" value="Button 2"          onclick="alert(form.b1.value);">   <!-- And the <form> is in the scope chain, so we can omit "form". -->   <input  type="button" value="Button 3"          onclick="alert(b4.value);">   <!-- The Document object is on the scope chain, so we can use its methods -->   <!-- without prefixing them with "document". This is bad style, though. -->   <input  type="button" value="Button 4"          onclick="alert(getElementById('b3').value);"> </form> 

As you can see from this sample code, the scope chain of an event handler does not stop with the object that defines the handler: it proceeds up the containment hierarchy and includes, at a minimum, the HTML <form> element that contains the button and the Document object that contains the form.[*] The final object in the scope chain is the Window object, because it always is in client-side JavaScript.

[*] The precise composition of the scope chain has never been standardized and may be implementation-dependent.

Another way to think about the extended scope chain of event handlers is to consider the translation of the JavaScript text of the HTML event handler attribute into a JavaScript function. Consider the following lines from the previous example:

 <input  type="button" value="Button 3"        onclick="alert(b4.value);"> 

The equivalent JavaScript code would be the following:

 var b3 = document.getElementById('b3'); // Find the button we're interested in b3.onclick = function( ) {     with (document) {         with(this.form) {             with(this) {                 alert(b4.value);             }         }     } } 

The repeated with statements create an extended scope chain. See Section 6.18. if you've forgotten about this infrequently used statement.

Having the target object in the scope chain of an event handler can be a useful shortcut. But having an extended scope chain that includes other document elements can be a nuisance. Consider, for example, that both the Window and Document objects define methods named open( ). If you use the identifier open without qualification, you are almost always referring to the window.open( ) method. In an event handler defined as an HTML attribute, however, the Document object is in the scope chain before the Window object, and using open by itself refers to the document.open( ) method. Similarly, consider what would happen if you added a property named window to a Form object (or defined an input field with name="window"). If you then define an event handler within the form that uses the expression window.open( ), the identifier window resolves to the property of the Form object rather than the global Window object, and event handlers within the form have no easy way to refer to the global Window object or to call the window.open( ) method!

The moral is that you must be careful when defining event handlers as HTML attributes. Your safest bet is to keep any such handlers very simple. Ideally, they should just call a global function defined elsewhere and perhaps return the result:

 <script>function validateForm( ) { /* Form validation code here */ }</script> <form onsubmit="return validateForm( );">...</form> 

A simple event handler like this is still executed using an unusual scope chain, but by keeping the code short, you minimize the likelihood that the long scope chain will trip you up. Once again, remember that functions are executed using the scope in which they were defined, not the scope from which they are invoked. So, even though our sample validateForm( ) method is invoked from an unusual scope, it is executed in its own global scope with no possibility for confusion.

Since there is no standard for the precise composition of the scope chain of an event handler, it is safest to assume that it contains only the target element and the global Window object. For example, use this to refer to the target element, and when the target is an <input> element, feel free to use form to refer to the containing Form object instead of this.form. However, don't rely on the Form or Document objects being in the scope chain; for example, don't use action instead of form.action or getElementById instead of document.getElementById.

Finally, keep in mind that this entire discussion of event-handler scope applies only to event handlers defined as HTML attributes. If you specify an event handler by assigning a function to an appropriate JavaScript event-handler property, there is no special scope chain involved, and your function executes in the scope in which it was defined. This is almost always the global scope, unless it is a nested function, in which case the scope chain can get interesting again!




JavaScript. The Definitive Guide
JavaScript: The Definitive Guide
ISBN: 0596101996
EAN: 2147483647
Year: 2004
Pages: 767

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