Section 17.4. Mouse Events


17.4. Mouse Events

Now that we've covered the three event models, let's look at some practical event-handling code. This section discusses mouse events in more detail.

17.4.1. Converting Mouse Coordinates

When a mouse event occurs, the clientX and clientY properties of the event object hold the position of the mouse pointer. This position is in window coordinates: it is relative to the upper-left corner of the browser's "viewport" and does not take document scrolling into account. You may often need to convert these values to document coordinates; for example, to display a tool tip window near the mouse pointer, you need document coordinates in order to position the tool tip. Example 17-3 is a continuation of the tool tip code of Example 16-4. Example 16-4 simply showed how to display a tool tip window at document coordinates that you specify. This example expands upon that one, adding a Tooltip.schedule( ) method that displays a tool tip at coordinates extracted from a mouse event. Since the mouse event specifies the position of the mouse in window coordinates, the schedule( ) method converts these to document coordinates using the Geometry module methods defined in Example 14-2.

Example 17-3. Tool tips positioned via mouse events

 // The following values are used by the schedule( ) method below. // They are used like constants but are writable so that you can override // these default values. Tooltip.X_OFFSET = 25;  // Pixels to the right of the mouse pointer Tooltip.Y_OFFSET = 15;  // Pixels below the mouse pointer Tooltip.DELAY = 500;    // Milliseconds after mouseover /**  * This method schedules a tool tip to appear over the specified target  * element Tooltip.DELAY milliseconds from now. The argument e should  * be the event object of a mouseover event. This method extracts the  * mouse coordinates from the event, converts them from window  * coordinates to document coordinates, and adds the offsets above.  * It determines the text to display in the tool tip by querying the  * "tooltip" attribute of the target element. This method  * automatically registers and unregisters an onmouseout event handler  * to hide the tool tip or cancel its pending display.  */ Tooltip.prototype.schedule = function(target, e) {     // Get the text to display. If none, we don't do anything.     var text = target.getAttribute("tooltip");     if (!text) return;     // The event object holds the mouse position in window coordinates.     // We convert these to document coordinates using the Geometry module.     var x = e.clientX + Geometry.getHorizontalScroll( );     var y = e.clientY + Geometry.getVerticalScroll( );     // Add the offsets so the tool tip doesn't appear right under the mouse.     x += Tooltip.X_OFFSET;     y += Tooltip.Y_OFFSET;     // Schedule the display of the tool tip.     var self = this;  // We need this for the nested functions below     var timer = window.setTimeout(function( ) { self.show(text, x, y); },                                   Tooltip.DELAY);     // Also, register an onmouseout handler to hide a tool tip or cancel     // the pending display of a tool tip.     if (target.addEventListener) target.addEventListener("mouseout", mouseout,                                                          false);     else if (target.attachEvent) target.attachEvent("onmouseout", mouseout);     else target.onmouseout = mouseout;     // Here is the implementation of the event listener     function mouseout( ) {         self.hide( );                // Hide the tool tip if it is displayed,         window.clearTimeout(timer); // cancel any pending display,         // and remove ourselves so we're called only once         if (target.removeEventListener)             target.removeEventListener("mouseout", mouseout, false);         else if (target.detachEvent) target.detachEvent("onmouseout",mouseout);         else target.onmouseout = null;     } } // Define a single global Tooltip object for general use Tooltip.tooltip = new Tooltip( ); /*  * This static version of the schedule( ) method uses the global tooltip  * Use it like this:  *  *   <a href="www.davidflanagan.com" tooltip="good Java/JavaScript blog"  *      onmouseover="Tooltip.schedule(this, event)">David Flanagan's blog</a>  */ Tooltip.schedule = function(target, e) { Tooltip.tooltip.schedule(target, e); } 

17.4.2. Example: Dragging Document Elements

Now that event propagation, event-handler registration, and the various event object interfaces for the DOM Level 2 and the IE event models have all been covered, it's time to put them together in a practical example. Example 17-4 shows a JavaScript function, drag( ), that, when invoked from a mousedown event handler, allows an absolutely positioned document element to be dragged by the user. drag( ) works with both the DOM and IE event models.

drag( ) takes two arguments. The first is the element that is to be dragged. This may be the element on which the mousedown event occurred or a containing element (e.g., you might allow the user to drag on the titlebar of a window to move the entire window). In either case, however, it must refer to a document element that is absolutely positioned using the CSS position attribute. The second argument is the event object associated with the triggering mousedown event.

drag( ) records the position of the mousedown event and then registers event handlers for the mousemove and mouseup events that follow the mousedown event. The handler for the mousemove event is responsible for moving the document element, and the handler for the mouseup event is responsible for deregistering itself and the mousemove handler. It is important to note that the mousemove and mouseup handlers are registered as capturing event handlers because the user may move the mouse faster than the document element can follow it, and some of these events occur outside the original target element. Without capturing, the events may not be dispatched to the correct handlers. Also, note that the moveHandler( ) and upHandler( ) functions that are registered to handle these events are defined as functions nested within drag( ). Because they are defined in this nested scope, they can use the arguments and local variables of drag( ), which considerably simplifies their implementation.

Example 17-4. Dragging document elements

 /**  * Drag.js: drag absolutely positioned HTML elements.  *  * This module defines a single drag( ) function that is designed to be called  * from an onmousedown event handler. Subsequent mousemove events will  * move the specified element. A mouseup event will terminate the drag.  * If the element is dragged off the screen, the window does not scroll.  * This implementation works with both the DOM Level 2 event model and the  * IE event model.  *  * Arguments:  *  *   elementToDrag: the element that received the mousedown event or  *     some containing element. It must be absolutely positioned. Its  *     style.left and style.top values will be changed based on the user's  *     drag.  *  *   event: the Event object for the mousedown event.  **/ function drag(elementToDrag, event) {     // The mouse position (in window coordinates)     // at which the drag begins     var startX = event.clientX, startY = event.clientY;     // The original position (in document coordinates) of the     // element that is going to be dragged. Since elementToDrag is     // absolutely positioned, we assume that its offsetParent is the     // document body.     var origX = elementToDrag.offsetLeft, origY = elementToDrag.offsetTop;     // Even though the coordinates are computed in different     // coordinate systems, we can still compute the difference between them     // and use it in the moveHandler( ) function. This works because     // the scrollbar position never changes during the drag.     var deltaX = startX - origX, deltaY = startY - origY;     // Register the event handlers that will respond to the mousemove events     // and the mouseup event that follow this mousedown event.     if (document.addEventListener) {  // DOM Level 2 event model         // Register capturing event handlers         document.addEventListener("mousemove", moveHandler, true);         document.addEventListener("mouseup", upHandler, true);     }     else if (document.attachEvent) {  // IE 5+ Event Model         // In the IE event model, we capture events by calling         // setCapture( ) on the element to capture them.         elementToDrag.setCapture( );         elementToDrag.attachEvent("onmousemove", moveHandler);         elementToDrag.attachEvent("onmouseup", upHandler);         // Treat loss of mouse capture as a mouseup event.         elementToDrag.attachEvent("onlosecapture", upHandler);     }     else {  // IE 4 Event Model         // In IE 4 we can't use attachEvent( ) or setCapture( ), so we set         // event handlers directly on the document object and hope that the         // mouse events we need will bubble up.         var oldmovehandler = document.onmousemove; // used by upHandler( )         var olduphandler = document.onmouseup;         document.onmousemove = moveHandler;         document.onmouseup = upHandler;     }     // We've handled this event. Don't let anybody else see it.     if (event.stopPropagation) event.stopPropagation( );  // DOM Level 2     else event.cancelBubble = true;                      // IE     // Now prevent any default action.     if (event.preventDefault) event.preventDefault( );   // DOM Level 2     else event.returnValue = false;                     // IE     /**      * This is the handler that captures mousemove events when an element      * is being dragged. It is responsible for moving the element.      **/     function moveHandler(e) {         if (!e) e = window.event;  // IE Event Model         // Move the element to the current mouse position, adjusted as         // necessary by the offset of the initial mouse-click.         elementToDrag.style.left = (e.clientX - deltaX) + "px";         elementToDrag.style.top = (e.clientY - deltaY) + "px";         // And don't let anyone else see this event.         if (e.stopPropagation) e.stopPropagation( );  // DOM Level 2         else e.cancelBubble = true;                  // IE     }     /**      * This is the handler that captures the final mouseup event that      * occurs at the end of a drag.      **/     function upHandler(e) {         if (!e) e = window.event;  // IE Event Model         // Unregister the capturing event handlers.         if (document.removeEventListener) {  // DOM event model             document.removeEventListener("mouseup", upHandler, true);             document.removeEventListener("mousemove", moveHandler, true);         }         else if (document.detachEvent) {  // IE 5+ Event Model             elementToDrag.detachEvent("onlosecapture", upHandler);             elementToDrag.detachEvent("onmouseup", upHandler);             elementToDrag.detachEvent("onmousemove", moveHandler);             elementToDrag.releaseCapture( );         }         else {  // IE 4 Event Model             // Restore the original handlers, if any             document.onmouseup = olduphandler;             document.onmousemove = oldmovehandler;         }         // And don't let the event propagate any further.         if (e.stopPropagation) e.stopPropagation( );  // DOM Level 2         else e.cancelBubble = true;                  // IE     } } 

The following code shows how you can use drag( ) in an HTML file (it's a simplified version of Example 16-3, with the addition of dragging):

 <script src="/books/2/427/1/html/2/Drag.js"></script> <!-- Include the Drag.js script --> <!-- Define the element to be dragged --> <div style="position:absolute; left:100px; top:100px; width:250px;             background-color: white; border: solid black;"> <!-- Define the "handle" to drag it with. Note the onmousedown   attribute.   --> <div style="background-color: gray; border-bottom: dotted black;             padding: 3px; font-family: sans-serif; font-weight: bold;"      onmousedown="drag(this.parentNode, event);"> Drag Me  <!-- The content of the "titlebar" --> </div> <!-- Content of the draggable element --> <p>This is a test. Testing, testing, testing.<p>This is a test.<p>Test. </div> 

The key here is the onmousedown attribute of the inner <div> element. Although drag( ) uses the DOM and IE event models internally, it's registered here using the Level 0 model for convenience.

Here's another simple example that uses drag( ); it defines an image that the user can drag if the Shift key is held down:

 <script src="/books/2/427/1/html/2/Drag.js"></script> <img src="/books/2/427/1/html/2/draggable.gif" width="20" height="20"      style="position:absolute; left:0px; top:0px;"      onmousedown="if (event.shiftKey) drag(this, event);"> 




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