6.3. Binding Event Handlers to ElementsThe first step in using events in a scriptable browser is determining which object and which event you need in order to trigger a scripted operation. With form elements, the choices are fairly straightforward, especially for mouse and keyboard events. For example, if you want some action to occur when the user clicks on a button object, you need to associate an onclick event handler with the button. The code that you add to your page to instruct an element to execute some script code in response to an event type performs what is called event binding. You have several ways to accomplish this vital task, a few of which work equally well in multiple DOMs. 6.3.1. Event Handlers as Tag AttributesPerhaps the most common way to bind an event handler to an element is to embed the handler in the HTML tag for the element. Regardless of the document type you declare at the top of your document, browsers allow all of their native event handlers to be specified as attributes of HTML tags. Browsers acknowledge attributes without respect to case, but this may change in the future. All-lowercase event handler attribute names will ultimately prevail, and they are backward-compatible with all scriptable browsers. If you intend to pass your pages through an HTML or XHTML validator, limit tag attribute event binding to the event types supported by specific elements in the W3C specification, as detailed in Appendix E. For XHTML validation, be sure attribute names are all lowercase. The value you assign to an event handler attribute is a string that can contain inline script statements: <input type="button" value="Click Here" onclick="alert('You clicked me!');"> Or it can be a function invocation: <input type="button" value="Click Here" onclick="handleClick( );"> Multiple statements within the value are separated by semicolons: <input type="button" value="Click Here" onclick="doFirst( ); doSecond( );"> You can pass parameter values to an event handler function, just as you would pass them to any function call, but there are also some nonobvious parameters that may be of value to an event handler function. For example, the this keyword is a reference to the element in whose tag the event handler appears. This technique is a backward-compatible way of conveying the target element reference to the function for early browsers that don't have an event object. In the following text field tag, the event handler passes a reference to that very text field object to a function named convertToUpper( ): <input type="text" name="CITY" onchange="convertToUpper(this);"> The function can then use that parameter as a fully valid reference to the object, for reading or writing the object's properties: function convertToUpper(field) { field.value = field.value.toUpperCase( ); } Once a generic function like this one is defined in the document, an onchange event handler in any text field element can invoke this single function with assurance that the result is placed in the changed field. The this reference can also be used in the event handler to convey properties from an object. For example, if an event handler function must deal with multiple items in the same form, it is useful to send a reference to the form object as the parameter and let the function dig into the form object for specific elements and their properties. Since every form element has a form property, you can pass an element's form object reference with the parameter of this.form: <input type="button" value="Convert All" onclick="convertAll(this.form);"> The corresponding function might assign the form reference to a parameter variable called form as follows: function convertAll(form) { for (var i = 0; i < form.elements.length; i++) { if (form.elements[i].type == "text") { form.elements[i].value = form.elements[i].value.toUpperCase( ); } } } If you bind an event handler to a tag attribute for use in Netscape 6 or later, you must explicitly pass the event object reference to the function. Do this by including the event keyword as the parameter (or one of the parameters): <input type="button" value="Click Here" onclick="handleClick(event);"> The trend is to migrate event binding away from element attributes and toward the other approaches described next. Well-constructed element structures lend themselves to allowing the event object—and its reference to the target element—fill in for any parameters that you might pass from an event handler attribute. Moving event handlers out of elements, however, makes it more difficult to study code (including your own, old code whose operation you've forgotten) to see quickly how events are handled in the page. 6.3.2. Event Handlers as Object PropertiesAs of Navigator 3 and Internet Explorer 4, an event handler can also be assigned to an object as a property of that object via a script statement. For every event that an object supports, the object has a property with the event handler name in all lowercase (although some browsers also recognize the intercapitalized version, as well). You use the standard assignment operator (=) to assign a function reference to the event handler. Because modern DOMs treat each script function as an object, a function reference is an unquoted name of a function, without the parentheses normally associated with the function name. For example, to have a button's onclick event handler invoke a function named handleClick( ) defined elsewhere in the document, the assignment statement is: document.forms[0].buttonName.onclick = handleClick; Notice, too, that the reference to the function name is case-sensitive. Be sure to preserve function name capitalization in its equivalent reference. Binding event handlers to objects as properties has advantages and disadvantages. One advantage is that you can use scripted branching to simplify the invocation of event handler functions that require (or must omit) certain browser versions. For example, if you implement an image-swapping mouse rollover atop a link surrounding an image, you can weed out old browsers that don't support image swapping by not assigning the event handler to those versions: if (document.images) { document.links[1].onmouseover = swapImage1; } Without an event handler specified in the tag, an older browser is not tripped up by the invalid object, and the image swapping function doesn't have to do the version checking. Moving event handler binding to script statements also means that you don't have to worry about HTML and XHTML validators tripping up on event handlers that are not defined in those standards. This is how you can employ a nonstandard event handler and still allow the page to pass formal validation. A minor disadvantage for the conversion of legacy scripts to the new format is that this approach does not let you pass parameters to functions. Netscape 6 or later automatically passes an event object as the lone parameter to the function, while IE passes none (because all event information is in IE's window.event object). It is up to the called function to derive element object information from the browser's event object, regardless of how it exposes itself to the function. At times more daunting, however, is the fact that event handler assignment statements must be executed after the script function and the bound element have loaded into the browser (and thus into the page's object model). This means that the assignment statement either must be physically below the element's tag in the document or it must run in a function invoked by the window's onload event handler. If the function or element object is not yet loaded, the assignment statement causes an error because the object does not yet exist and the reference to the object fails. This doesn't mean that you can't assign event handlers by script to run immediately as the page loads. But you must choose your targets carefully. Assigning events to the window and document objects is safe in such statements (after the functions, that is) because those two objects are valid immediately. Some browsers also assume the existence of the document.body object while scripts in the head execute during page loading, but that behavior is not universal. Your page and script design may also allow you to define event handlers at the document level, and let events from elements bubble up to the document (see Section 6.5 later in this chapter). The onus is then on the function to examine the event object and process events from intended targets, while ignoring events from elsewhere. Note, too, that event handler assignment allows you to invoke only one function per event type per element object. Unlike the tag attribute approach, which lets you chain together a semicolon-delimited series of script expressions, you get only one shot at reference assignment. If your page is set up with multiple assignments of the same element object and event property, the last one to load is the winner. For example, the DHTML API library from Chapter 4 contains its own window.onload event handler assignment to ensure that the initDHTMLAPI( ) function is called automatically. But in Examples 4-4 and 4-5, an onload event handler in the <body> tag (the tag equivalent of assigning window object event handlers) invokes both the initDHTMLAPI( ) function and some other application-specific function. The tag assignment overrides the assignment from the external library. Alternatively, these pages could create one more startup function that invokes both initDHTMLAPI( ) and the initial action function, and that startup function reference could be assigned to window.onload at the bottom of the head section's scripts. Tag attribute and object property assignment are the two event binding techniques that work best across all browsers. The remaining three techniques are limited to individual DOM implementations and are not easy to branch around. 6.3.3. Event Handlers as <script> Tags (IE 4 and Later)The third technique for binding event handlers to objects currently works only in Internet Explorer 4 and later (for all operating system platforms). The technique uses two proprietary attributes (for and event) in the <script> tag to specify that the script is to be run in response to an event for a particular object. The for attribute points to an id attribute value that is assigned to the element that generates the event handler; the event attribute names the event handler. Internet Explorer does not attempt to resolve the for attribute reference while the document loads, so it is safe to put the tag before the element in the source code.[7]
The following fragment shows what the entire <script> tag looks like for the function defined earlier that converts all of a form's element content to uppercase in response to a button's onclick event handler: <script for="upperAll" event="onclick" language="JavaScript" type="text/javascript"> var form = document.forms[0]; for (var i = 0; i < form.elements.length; i++) { if (form.elements[i].type == "text") { form.elements[i].value = form.elements[i].value.toUpperCase( ); } } </script> The HTML for the button does not include an event handler, but does require an id (or name) attribute. <input type="button" id="upperAll" value="Convert All"> Do not use this technique in pages that might be viewed by non-IE browsers. The extra attributes tell IE to defer script execution until invoked by the event type on a certain element. A non-IE browser treats the script statements as if they existed in plain <script> tags, and will execute while the page loads. Script errors are sure to arise in non-IE browsers. Note that you might see a variation of this technique for defining scripts directly as event handlers when the scripting language is specified as VBScript. Instead of specifying the object name and event as tag attributes, VBScript lets you combine the two in a function name, separated by an underscore character, as in: <script language="VBScript" type="text/vbscript"> Function upperAll_onclick script statements End Function </script> The tag for the element requires only the id attribute to make the association. 6.3.4. Attaching Events (IE 5 and Later for Windows)Microsoft devised the attachEvent( ) and detachEvent( ) methods of element objects primarily to support a feature it calls behaviors (external XML documents that contain generic script definitions, not unlike the concept of style sheets). But in browsers that support these methods, you can use them to bind events to element objects. The attachEvent( ) method requires two parameters: elementReference.attachEvent("event", functionReference); The event parameter is the "on" version of the event name, while the function reference is just like the kind you assign to an object event handler property. The combination of attachEvent( ) and detachEvent( ) allows scripts to enable and disable scripted functionality as desired. Although Microsoft has submitted HTML behaviors as a proposed W3C standard, any future standardized implementation details are difficult to predict. Within the confines of supported IE versions, this syntax is perfectly acceptable. 6.3.5. W3C Event Listeners (Netscape 6 and Later)The W3C DOM's Events module introduces fresh terminology to event binding, but the concepts behind the new words are not new. In line with the object-oriented nature of the W3C DOM, two node object methods, addEventListener( ) and removeEventListener( ), which add and remove, respectively, the power to "hear" an event of a particular type as it passes by the node during event propagation (described later in this chapter). Parameters for both methods are the same, so we'll focus on how to perform the event binding portion. The syntax for the addEventListener( ) method is: elementReference.addEventListener("eventType", functionReference, captureSwitch); An eventType value is a string indicating the formal event type, which is the event name without the "on" prefix (i.e., just click instead of onclick). A function reference is the same kind that you use for object property event binding. The W3C DOM jargon calls this kind of function an event listener function, which means little more than the function should have a parameter variable to receive the event object that automatically gets passed to it. The third parameter is a Boolean value that determines whether the node should "listen" for the event in the capture portion of event propagation (described later in this chapter). The typical setting of this parameter is false. To remain true to the W3C model, the specification permits browsers to accept traditional event binding mechanisms, including tag attributes. Such bindings are to behave as if the code invokes addEventListener( ) with the third parameter automatically set to false. This flexibility allows a browser such as Netscape 6 to implement the W3C DOM model, while allowing scripters to use event binding syntax that is compatible with other browsers, including older versions. But by using the newer syntax, you can explore several new event types that are linked directly to the W3C DOM's architecture. See Chapter 10 for more details on W3C DOM events and event object properties. Copyright © 2003 O'Reilly & Associates. All rights reserved. |
|