Section VI.3. Binding Events to Elements


VI.3. Binding Events to Elements

The 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 control 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 a click event 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.

VI.3.1. Events as Tag Attributes

Perhaps the most backward-compatible way to bind an event 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 events to be specified as attributes of HTML tags. All-lowercase event attribute names are both backward- and forward-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 of Dynamic HTML, Third Edition. For XHTML validation, event attribute names must be all lowercase.

The value you assign to an event 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 function. For example, the this keyword is a reference to the element in whose tag the event 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 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, a change event 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 to convey properties from the target object. For example, if an event function must deal with multiple items in the same form, it may be 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 object 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 to a tag attribute for use in browsers that support the W3C DOM Event model (e.g., Mozilla, Safari, Opera) and if the function needs to inspect event object proeprties, 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 objectand its reference to the target elementfill in for any parameters that you might pass from an event handler attribute. Moving event binding out of elements, however, makes it more difficult to study code (including your old code whose operation you've forgotten) to see quickly how events are handled in the page.

VI.3.2. Events as Object Properties

Starting back with Navigator 3 and Internet Explorer 4, an event handler function could also be bound to an element 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 (the form with the leading "on") in all lowercase (although some browsers also recognize the lowerCamelCase version, as well). You use the standard assignment operator (=) to assign a function reference to the event. 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 invoking the function. For example, to have a button's click event invoke a function named handleClick( ) defined elsewhere in the document, the property 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 events to objects as properties has advantages and disadvantages. One advantage is that you can use scripted branching to simplify the invocation of event functions that require (or must omit) certain browser versions. For example, if you use W3C DOM element referencing to implement an image-swapping mouse rollover atop a link surrounding an image, you can weed out old browsers that don't support the W3C DOM syntax by not assigning the event to those versions:

 if (document.getElementById) {     document.links[1].onmouseover = swapImage1; } 

Without an event specified in the tag, an older browser is not tripped up by unknown syntax, and the image swapping function doesn't have to do the version checking.

Moving event binding to script statements also means that you don't have to worry about HTML and XHTML validators tripping on event attributes that are not defined in those standards. This is how you can employ a nonstandard event and still allow the page to pass formal validation.

You can still pass parameters to event handlers bound as properties, but you need to use an anonymous function as a go-between. In the following event assignment statement, an element's click event is bound to the handleClick( ) function while passing both the W3C DOM event object and the value of a local variable named myDogsName:

 function initEvents( ) {     var myDogsName = document.pets.name.value;     document.getElementById("callDog").onclick =         function (evt) {handleClick(evt, myDogsName);};     ... } 

Using a similar construction, you can bind multiple event handler functions to a single event as in the following:

 elementReference.onclick = function (evt) {     handleClick1(evt);     handleClick2(evt);}; 

Or, if it makes sense to your scripting style, assign an anonymous function that is the complete event handler function. This eliminates the need to clutter the global naming space with a separate function name. The following example changes the className property of an element whose ID contains the word "banner" to effect a style change when a mousedown event occurs in it:

 document.onmousedown = function(evt) {     var evt = (evt) ? evt : window.event;     var elem = (evt && evt.target) ? evt.target : evt.srcElement;     if (elem.nodeType == 3) {elem = elem.parentNode;}     if (elem.id && elem.id.match(/banner/)) {         elem.className = "bannerHilite";     } } 

A minor disadvantage over tag attribute binding, however, is the fact that event assignment statements must be executed after the script function and the bound element have loaded into the browser. That is to say, the element must exist in the object model before you can bind an event to it via a script statement. 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 load event. If the function or element objects are not yet loaded, the assignment statement causes an error because an object does not yet exist and the reference to the object fails.

This doesn't mean that you can't assign events 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 and should not be relied upon. Your page and script design may also allow you to define events at the document level, and let events from elements bubble up to the document (see "Event Propagation," later in this Online Section). The onus is then on the function to examine the event object and process events from intended targets, while ignoring events from elsewhere.

Tag attribute and object property assignment are the two event binding techniques that work best across all browsers. Even so, these approaches are falling out of favor in modern scripting. They are being supplanted by the Internet Explorer event attachment and W3C DOM event listener mechanisms.

VI.3.3. 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 Microsoft now recommends using this event binding mechanism over all others for Internet Explorer.

The attachEvent( ) method requires two parameters:

 elementReference.attachEvent("event", functionReference); 

The string event parameter is the "on" version of the event name, while the function reference is just like the kind you assign to an element object's event property, which means the value can be a reference to an existing function (i.e., the function name without the parentheses) or an anonymous function defined on the spot. The combination of attachEvent( ) and detachEvent( ) allows scripts to enable and disable scripted functionality as desired.

You may invoke the method multiple times for the same element and event type. Thus, you can essentially queue up multiple event handlers for the same event. Note that such multiple event handlers are executed in the reverse order in which they are assigned (first-in, last-to-execute).

VI.3.4. W3C Event Listeners (Mozilla, Safari, Opera)

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( ), 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 Online Section). Parameters for both methods are the same, so we'll focus on how to perform the event binding portion.

Events as <script> Tags (IE 4 and Later)

Microsoft designed an event binding technique that is supported by 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; the event attribute names the event. 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.

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 click event:

 <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, but does require an id (or name) attribute.

 <input type="button"  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 exist 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 events 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.


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, including an anonymous function, if desired. The W3C DOM jargon calls the function invoked by an event listener 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.

As with Microsoft's attachEvent( ) method, you may queue up multiple event listener functions to be invoked for the same event targeting the same node. Unlike attachEvent( ), however, a given event invokes associated event listener functions in the order in which they were added (first-in, first-executed).

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 Mozilla 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 3 of Dynamic HTML, Third Edition for more details on W3C DOM events and event object properties.

VI.3.5. Binding Multiple Events

If you are designing either a big project, or one that relies on several .js libraries, each of which needs initialization after the document loads (because they rely on element references that likely won't be valid until the browser has interpreted all of the HTML markup), you will want to bind multiple function calls to the window's load event. The typical ad hoc way to accomplish this is to assemble a customized initialization function in the document that invokes the desired functions, as in the following example:

 function init( ) {     initModule1( );  // from module1.js     initModule2( );  // from module2.js } window.onload = init; 

This approach, however, means that you'll have to change every such initialization function in every document of your project if you add a new module or change the name of the initialization routine in any one of them. That's not an appealing maintenance prospect.

Instead, you can queue up event handler function calls in such a way that each module simply adds its own initialization routine to the queue as it loads. When the load event fires, the calls to handler functions are made in the order in which they added themselves to the queue. The following example of a queue-building function should be among the first scripts loaded in a document (before any other .js files load):

 function addOnLoadEvent(func) {     var oldQueue = (window.onload) ? window.onload : function( ) {};     window.onload = function( ) {         oldQueue( );         func( );     } } 

Then, inside a .js library, and after the library's initialization function is defined, a single statement invokes the function, passing a reference to the initialization function, as in:

 addOnLoadEvent(dragLibInit); 

This function works reliably only if you do not have any other onload event bindings elsewhere in your documents through other means. In other words, do not attempt to mix this function with a page that has an onload attribute set in the <body> tag.

If you want to use the attachEvent( ) and addEventListener( ) methods where available, you can extend the addOnLoadEvent( ) function to accommodate those methods, as well as support older browsers (e.g., Netscape 4 and IE 4) with the code shown above. The following example provides not only that all-encompassing function, but some sample event handler functions and calls to addOnLoadEvent( ) that demonstrate how to pass parameters to an event handler function:

 // sample functions function do1( ) {     alert("Doing 1"); } function do2( ) {     alert("Doing 2"); } function do3(arg) {     alert("Doing " + arg); } // generic load event binder function addOnLoadEvent(func) {     if (window.addEventListener) {         window.addEventListener("load", func, false);     } else if (window.attachEvent) {         window.attachEvent("onload", func);     } else {         var oldQueue = (window.onload) ? window.onload : function( ) {};         window.onload = function( ) {             oldQueue( );             func( );         }     } } // sample bindings addOnLoadEvent(do1); addOnLoadEvent(do2); addOnLoadEvent(function( ) {do3("whatever I want.");}); 

One important hazard of using the combination of methods shown above is that, as noted earlier, the IE attachEvent( ) method causes queued calls to event handler functions to occur in the reverse order of the other event bindings operating in the function. If your libraries have dependencies on loading order, this discrepancy can cause problems. In that case, you may prefer to go with the simpler function queue approach shown earlier.

You can also extend this load event mechanism to other event types, and even build a generic function that accepts additional parameters to reference any desired element objects. Example VI-1 demonstrates a small library (eventsManager.js) that combines the window load event queue mechanism with functions that add and remove events for other element objects. It includes code that allows the library to work with older browsers, such as IE/Mac and Netscape 4 or earlier. This library is loaded into examples later in this chapter.

Example VI-1. eventsManager.js Library

 function addEvent(elem, evtType, func, capture) {    capture = (capture) ? capture : false;    if (elem.addEventListener) {       elem.addEventListener(evtType, func, capture);    } else if (elem.attachEvent) {       elem.attachEvent("on" + evtType, func);    } else {       // for IE/Mac, NN4, and older       elem["on" + evtType] = func;    } } function removeEvent(elem, evtType, func, capture) {    capture = (capture) ? capture : false;    if (elem.removeEventListener) {       elem.removeEventListener(evtType, func, capture);    } else if (elem.attachEvent) {       elem.detachEvent("on" + evtType, func);    } else {       // for IE/Mac, NN4, and older       elem["on" + evtType] = null;    } } function addOnLoadEvent(func) {     if (window.addEventListener || window.attachEvent) {         addEvent(window,"load", func, false);     } else {         var oldQueue = (window.onload) ? window.onload : function( ) {};         window.onload = function( ) {             oldQueue( );             func( );         }     } } 




Dynamic HTML. The Definitive Reference
Dynamic HTML: The Definitive Reference
ISBN: 0596527403
EAN: 2147483647
Year: 2004
Pages: 120
Authors: Danny Goodman

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