Приглашаем посетить
Ларри (larri.lit-info.ru)

Event Examples

Event Examples

The two sample programs in this section illustrate the power of the event architecture explained in this chapter. With the first example, the Event Tutor, you can test any page and see the events that fire when you interact with it. Code in the second example, the Event Broadcaster, provides a general mechanism for hooking up several handlers to each event.

Event Tutor

To help you learn more about event bubbling, the Chapter 3 samples on the companion CD include an HTML document named tutor.htm that can report all events as they occur on a page. Figure 3-2 shows the Event Tutor application.

Event Examples

Figure 3-2. The Event Tutor application.

This example allows you to select which events to track in the document contained within the right frame. When a selected event occurs on any element in the right frame, it is reported in the text box in the left frame. Playing with this example will clearly demonstrate for you how events bubble up through the hierarchy.

Following is the source code from events.htm used to create the Event Tutor. This code demonstrates tracking events across frames—a technique discussed in detail in Chapter 5, "Window and Frame Management." This code also takes advantage of JavaScript's model for exposing objects as associative arrays and the ability to create custom functions.

<HTML>
   <HEAD>
      <TITLE>Event Tutor</TITLE>
      <STYLE TYPE="text/css">
         caption {font-weight:bolder; color:navy}
      </STYLE>
      <SCRIPT LANGUAGE="JavaScript">
         function outputEvent(src, eventName) {
            // Append event name to text area control.
            document.all.txtEvents.value += eventName + ": " +
               src.tagName + "\n";
         }

         function setupEvents() {
            // The user clicked on a check box.
            // Hook up or remove event handlers.
            if ("checkbox" == event.srcElement.type) {
               var handler = event.srcElement.checked ?
                  new Function("outputEvent(this, '" +
                     event.srcElement.id + "')") :
                  null;
               var allSample = parent.frames.sample.document.all;
               // Add custom event handler to all elements in
               // the other frame.
               for (var intLoop=0; intLoop < allSample.length;
                     intLoop++) {
                  // Accesses the event property that matches
                  // the ID of the check box that was clicked.
                  allSample[intLoop][event.srcElement.id] = handler;
               }
            }
         }
      </SCRIPT>
   </HEAD>
   <BODY>
      <FORM NAME="EVENTS">
         <TABLE WIDTH=100% ONCLICK="setupEvents()" CELLPADDING=4>
            <CAPTION>Events</CAPTION>
            <TR VALIGN="Top"><TD NOWRAP>
               <!-- Notice the naming convention used below.
                    To add more events, the ID should specify
                    the event name. -->
               <INPUT TYPE=CHECKBOX ID=onmousedown>
               <LABEL FOR=onmousedown>MouseDown</LABEL><BR>
               <INPUT TYPE=CHECKBOX ID=onmouseover>
               <LABEL FOR=onmouseover>MouseOver</LABEL><BR>
               <INPUT TYPE=CHECKBOX ID=onmousemove>
               <LABEL FOR=onmousemove>MouseMove</LABEL><BR>
               <INPUT TYPE=CHECKBOX ID=onmouseout>
               <LABEL FOR=onmouseout>MouseOut</LABEL><BR>
               <INPUT TYPE=CHECKBOX ID=onmouseup>
               <LABEL FOR=onmouseup>MouseUp</LABEL><BR>
               <INPUT TYPE=CHECKBOX ID=onkeydown>
               <LABEL FOR=onkeydown>KeyDown</LABEL><BR>
               <INPUT TYPE=CHECKBOX ID=onkeypress>
               <LABEL FOR=onkeypress>KeyPress</LABEL><BR>
               <INPUT TYPE=CHECKBOX ID=onkeyup>
               <LABEL FOR=onkeyup>KeyUp</LABEL>
            </TD><TD NOWRAP>
               <INPUT TYPE=CHECKBOX ID=onselect>
               <LABEL FOR=onselect>Select</LABEL><BR>
               <INPUT TYPE=CHECKBOX ID=onselectstart>
               <LABEL FOR=onselectstart>SelectStart</LABEL><BR>
               <INPUT TYPE=CHECKBOX ID=onclick>
               <LABEL FOR=onclick>Click</LABEL><BR>
               <INPUT TYPE=CHECKBOX ID=ondblclick>
               <LABEL FOR=ondblclick>DblClick</LABEL><BR>
               <INPUT TYPE=CHECKBOX ID=onfocus>
               <LABEL FOR=onfocus>Focus</LABEL><BR>
               <INPUT TYPE=CHECKBOX ID=onblur>
               <LABEL FOR=onblur>Blur</LABEL><BR>
               <INPUT TYPE=CHECKBOX ID=onchange>
               <LABEL FOR=onchange>Change</LABEL><BR>
               <INPUT TYPE=CHECKBOX ID=ondragstart>
               <LABEL FOR=ondragstart>DragStart</LABEL>
            </TD></TR>
         </TABLE>
         <!-- TextArea to output event sequence -->
         <TEXTAREA ID="txtEvents" STYLE="width:95%" ROWS=14>
         </TEXTAREA>
      </FORM>
   </BODY>
</HTML>

You can experiment with this code on any document by copying it from the CD to your hard drive. Replace the sample.htm file with any file of your choosing. You can also run the example on the CD by opening the tutor.htm file, which uses both the sample.htm and the events.htm files.

Event Broadcaster

The event model exposed by Dynamic HTML is generally limited to a one-to-one relationship between event and event handler. However, as is demonstrated throughout this book, there will be many times when you need to associate multiple actions with a single event. This association can be accomplished by writing a routine for the event that calls each action in sequence, or this whole process can be automated by taking advantage of JavaScript's function pointers.

The Event Broadcaster generalizes the event binding used by Dynamic HTML to support a registration mechanism that can be used to bind multiple actions to a single event. This program provides a small, reusable set of functions that allow multiple actions to be bound to each event. Each of these actions can also execute conditionally.

By taking advantage of this code, you can write reusable event handlers that can be easily plugged into any Web page without having to rewire any other code. This technique works by using a registering model to allow functionality to register an element with a particular action. The registry takes the place of the developer manually hooking up event code.

While this mechanism greatly simplifies the writing of reusable code snippets, you must still be careful that multiple actions on a single event handler or document do not collide. There is no algorithmic way for you to provide this protection other than to be careful when adding new functionality. To be most effective, interactions between registered functions for the same event should be avoided.

The following code represents the entirety of the Event Broadcaster registry. This code can be written only in languages that support the dynamic creation of functions—therefore, this functionality cannot be implemented in VBScript. However, this code does not prevent you from supplying an action and registering an event handler written in VBScript.

<SCRIPT LANGUAGE="JavaScript">
   // Event Broadcaster Registry Code
   // This code generically binds multiple event handlers to
   // a single event.

   function runHandler(eventName, eventSrc) {
      // This is a generic event handler. For any event, this function
      // validates the condition and runs the appropriate code.

      var src = event.srcElement;
      // First check the srcElement property.
      for (var intLoop = 0;
            intLoop < eventSrc.manager[eventName].length; intLoop++)
         if (eventSrc.manager[eventName][intLoop].condition(src))
            eventSrc.manager[eventName][intLoop].doAction(src);

      src = src.parentElement;
      // Walk the tree; stop at the source element for the event.
      // tagName is null for the document; walk up entire tree.
      var top = (this.tagName == null) ? "HTML" : this.tagName;
      while (top != src.tagName) {
         for (var intLoop = 0;
               intLoop < eventSrc.manager[eventName].length;
               intLoop++)
            if (eventSrc.manager[eventName][intLoop].condition(src) &&
                  eventSrc.manager[eventName][intLoop].doTree)
               eventSrc.manager[eventName][intLoop].doAction(src);
         src = src.parentElement;
      }
   }

   function setupHandler(eventName, eventSrc) {
      // Create a new function handler for the event.
      eventSrc[eventName] =
         new Function("runHandler('" + eventName + "', this);");
   }

   function alwaysTrue() {
      // Use this function when you don't want to check any condition.
      return true;
  }

   function register(eventName, action) {
      // This is the generic routine to register the event.
      // Parameters (in order):
      //  eventName            - Event to bind to
      //  action               - Code to run when the event occurs
      //  condition (optional) - Condition to test to perform the
      //                         action; defaults to true
      //  doTree (optional)    - Determines whether to walk up all
      //                         nodes of the tree; defaults to false
      //  eventSrc (optional)  - Element the event is associated with;
      //                         defaults to the document

      // Determine the source element.
      var eventSrc = (null != arguments[4]) ?
         document.all[arguments[4]] : document;

      // Check whether an event manager exists on the object.
      if (null == eventSrc.manager)
         eventSrc.manager = new Object;
      // Check whether an event manager exists for the specific event.
      if (null == eventSrc.manager[eventName]) {
         eventSrc.manager[eventName] = new Object;
         eventSrc.manager[eventName].length = 0;
         setupHandler(eventName, eventSrc);
      }

      // Add the event handler.
      var ct = eventSrc.manager[eventName].length++;
      eventSrc.manager[eventName][ct] = new Object;
      eventSrc.manager[eventName][ct].doAction = action;
      // Check whether condition is supplied. If not, use alwaysTrue.
      eventSrc.manager[eventName][ct].condition =
         (null != arguments[2]) ? arguments[2] : alwaysTrue;
      // Check whether the tree is to be walked. Default to false.
      eventSrc.manager[eventName][ct].doTree =
         (null != arguments[3]) ? arguments[3] : false;
   }

   function hookupEvents() {
      var bindings = document.all.tags("BINDEVENT");
      for (var intLoop = 0; intLoop < bindings.length; intLoop++) {
         var bind = bindings[intLoop];
         if ((null != bind.getAttribute("event")) &&
               (null != bind.getAttribute("action"))) {
            var bEvent = bind.getAttribute("event");
            var bAction = new Function("return " +
               bind.getAttribute("action") +
               "(arguments[0])");
            var bCondition =
               (null == bind.getAttribute("condition")) ?
                  null :
                  new Function("return " +
                     bind.getAttribute("condition") +
                     "(arguments[0])");
            var bTree = ("walk" == bind.getAttribute("tree"));
            var bSrc = bind.getAttribute("for");
            register(bEvent, bAction, bCondition, bTree, bSrc);
         }
      }
   }

   window.onload = hookupEvents;
</SCRIPT>

This code takes advantage of many of the features of Dynamic HTML and JavaScript. All the techniques used in this example are covered in later chapters. For example, HTML does not define or support the <BINDEVENT> tag. Instead, Dynamic HTML exposes unrecognized elements in the object model. You can use this feature to associate information and extend scripts without having to modify any code.

To use this code, write an action function and register it with an event of a particular object on the page. The following code demonstrates how dynamic effects can be added to and registered in the preceding binding service. Dynamically changing the style of an element is discussed in Chapter 11, "Dynamic Styles."

<SCRIPT LANGUAGE="JavaScript">
   // Dynamic style mouseover effect

   function swapEffects(src) {
      // If an effect is supplied, swap it with className.
      if (null != src.getAttribute("effect")) {
         var tempClass = src.className;
         src.className = src.getAttribute("effect");
         src.setAttribute("effect", tempClass);
      }
   }

   function checkEffect(src) {
      // Condition to check for before swapping the effect
      return (src.getAttribute("effect") != null);
   }
</SCRIPT>

This script defines the action and condition for swapping effects with the class attribute. The following HTML binds this code to the onmouseover and onmouseout events of the document:

<BINDEVENT event="onmouseover" action="swapEffects" 
   condition="checkEffect" tree="walk">
<BINDEVENT event="onmouseout" action="swapEffects" 
   condition="checkEffect" tree="walk">

The custom event binding is powerful in that the author does not need to understand how to hook up code. Instead, the author can simply paste the code into the page. With easy-to-use HTML, the code can be associated with any event of any object. The real power of this model is revealed if the user tries to hook up another action to the onmouseover or onmouseout event. Normally, this would require writing custom onmousemove or onmouseout event handlers to call the different actions in sequence. With the method demonstrated here, all that is necessary is to paste the new function to be executed into the document and to hook it up to the event using another <BINDEVENT> tag. The registry code automatically manages the correct sets of event handlers and ensures that they are fired for each event.

An extra for attribute is supported on the <BINDEVENT> tag for associating the event directly with a specific element. By default, the event is attached to the document. The for attribute takes the id of the element that the event is being bound to.

[Содержание]