Professional JavaScript | 34 | WebReference

Professional JavaScript | 34

To page 1To page 2current page
[previous]

Professional JavaScript

Managing Events in the Handler Hierarchy

If a given document has no event handlers, then all the event flow and structure provided by the browser has no effect. So, leaving event handlers out is a good way to avoid complexity. This isn't very practical if you are designing a highly interactive page.

If you restrict yourself to handlers that are attached to the most-specific HTML tags only, you avoid event flow effects up or down the hierarchy and still get to receive events. In this case you are operating in a similar manner to the 3.0 browser event model, except more events are possible.

If you need to use events at several levels of the object model, the real differences between Netscape and Microsoft show up. A different approach must be adopted for each brand if you want to fully exploit the object hierarchy. The general strategy for each browser type can be described this way:

Microsoft

Managing events for Microsoft's 4.0 browser proceeds in the familiar pattern of previous browsers, so there's nothing radical to learn. When events proceed through handlers in the new event hierarchy, they do so one at a time - only one handler is active at any time. Two of the new properties of the window.event object need to be particularly borne in mind:

  • cancelBubble. This property is a true/false value that controls what happens to the event after the current handler has finished. If you want the current handler to be the last one that services the event caught, set this property to true. Beware that Microsoft documentation says that a few events cannot be cancelled! This example illustrates canceling, it displays MIDDLE and INNER alert boxes only:

    <HTML><BODY>
    <DIV ONCLICK="alert('OUTER'); return true;">
      <DIV ONCLICK="alert('MIDDLE'); window.event.cancelBubble=true; return true;">
      <DIV ONCLICK="alert('INNER'); window.event.cancelBubble=false; return true;">
         Click Me
      </DIV>
      </DIV>
    </DIV>
    </BODY></HTML>
  • returnValue. This property is a true/false value that controls whether the normal action of the event occurs (for example, submitting a form or navigating a link). Beware that actions of some events cannot be stopped, such as the unloading of a document associated with an onUnLoad event. The action for an event occurs after the last handler has finished. It is the return value of that last handler that normally dictates whether the action will happen, as for 3.0 browsers. This presents a problem if an earlier handler wants to have a say in the event's ultimate impact. The returnValue property provides an override mechanism for those earlier handlers. If it is ever set to true or false, so that it is no longer undefined, its value is used regardless of the return value of the last handler. This example will not follow the link even though all handlers return true, although it will pop up an alert box:

    <HTML><BODY>
    <A HREF="about:blank" ONCLICK="alert('A tag'); return true;">
     <DIV ONCLICK="alert('DIV tag'); window.event.returnValue=false; return true;">
    Click Me
    </DIV>
    </A>
    </BODY></HTML>

The bubbling and event action mechanisms are separate and don't affect each other.

Netscape

For Netscape, ordinary event handlers proceed in the same pattern as for 3.0 browsers, but the advanced features require a whole new concept. This concept is that window, document and layer objects can be interposed between the user and the object that an event is normally associated with, such as a button. The event may then be handled by this interposed object first. A general term for this kind of behavior is filtering, but it's just as easy to think of it as events arriving at the top of the document object model first, as described above. To do this requires three steps:

  • Create handlers for the interposing object. It is better to create and assign these directly in JavaScript, because their meaning in the web page is not as trivial as typical event handlers. When one of these handlers is called and then finishes, that is the end of all event processing for that event. Therefore, the return value of that handler dictates whether the event's action is canceled or not, as for earlier browser versions. If tag attributes are not used to define these handlers, the appropriate properties of the window, document or layer object must be assigned to.

  • Start collecting events. An interposed object does not automatically use these new handlers. The special function captureEvents() must be used to determine which events an interposed object will collect. The sole argument is a bitwise-OR'ed list of constants. These constants are similar to other JavaScript constants such as Math.PI. They represent the ten general user input events specified by HTML 4.0. Event.MOUSEUP | Event.MOUSEDOWN | Event.KEYPRESS is an example of three values bitwise-OR'ed together. Only the events specified in the bitmask are affected by the use of this function. The matching function releaseEvents() stops an interposed object from collecting events.

  • Decide routing policy. Once the interposed object is collecting events, those events will never be forwarded to their original destination (such as a form element) unless extra efforts are made. These efforts takes the form of two methods: routeEvent() and handleEvent(). A call to routeEvent() from within an event handler tells the browser to suspend the current handler, call other handlers that are involved and then return back to the point of suspension. So routeEvent() provides the mechanism for events to "route down" the object hierarchy. This behavior means handlers are nested - one handler is invoked from inside another. The other function handleEvent() is more radical. It is a method of the Window and Document (not Layer) objects. If such an object's method is called from within another handler then the event is passed to that object directly, ignoring any other objects expecting the event, and ignoring the normal hierarchy entirely.

Here is an example of two frames that illustrate a simple case of handleEvent(). To see it happen, click in either frame. All that this example does is to interpose a window between the two documents that each occupy on of the frames in the window. This is like putting two diamonds behind bulletproof glass - first you have to get through the glass. To illustrate routeEvent(), add a button to each frame with an onClick handler, and change top.first.handleEvent(e) to read routeEvent(e).

<!-- frameset.htm -->
<HTML>
<FRAMESET ROWS="*,*">
<FRAME NAME="first" SRC="anyframe.htm">
<FRAME NAME="second" SRC="anyframe.htm">
</FRAMESET>
</HTML>
<!-- anyframe.htm -->
<HTML><BODY><SCRIPT>
document.write('This is the frame named "'+self.name+'"');  // visible content
function handle_it(e)                // ordinary event handler, e is the event
{
  alert("Caught by frame: " + window.name);
  if ( window.name == "first" )
    alert("Preventing infinite loop");
  else
    return top.first.handleEvent(e);
  return true;
}
window.onMouseDown = handle_it;                              // assign handler
window.captureEvents(Event.MOUSEDOWN);  // in this case, filter just one event
</SCRIPT></BODY></HTML>

There is one complication that is due to frames. A frame might display a URL from a web site that is different to the website of the frameset document. A security hobble prevents parent documents from capturing that frame's events (more on this in Chapter 5). However, if the parent document and its scripts are signed or otherwise secure, the methods window.enableExternalCapture() and window.disableExternalCapture() can be used to free up or re-impose this restriction.

To page 1To page 2current page
[previous]


Created: April 4, 2001
Revised: April 4, 2001

URL: http://webreference.com/programming/javascript/professional/chap4/4/