Section VI.7. Dragging Elements


VI.7. Dragging Elements

The final example in this chapter, Example VI-3, demonstrates binding and unbinding events as needed for the purpose of dragging and dropping elements. Although earlier editions of this book included code to work in Netscape Navigator 4's outdated layer environment, Example VI-3 employs techniques that preclude Netscape 4 from allowing the dragging operations to work. In particular, Netscape does not allow scripts to find elements in a document based on their class attributesa facility that puts less of a burden on programmers to denote which elements are draggable, without imposing more rigid (and prone to maintenance breakage) element naming demands. With this library, all you need to do to make an element draggable is to use CSS to turn it into an absolute-positioned element, and then include the draggable identifier in the element's class attribute (remember that you can assign multiple class names to an element). The library is compatible with browsers starting with IE 4/Windows, IE 5/Mac, Mozilla 1.0, Opera 5, and Safari.

The only other requirements for Example VI-3 are two .js libraries that you've seen earlier in this book. One is the DHTMLapi3.js library from Online Section V; the other is the eventsManager.js library from earlier in this chapter. Although Example VI-3 shows the dragging code within the document for the sake of convenience, you would normally put this code, too, into its own external .js library.

This version implements all dragging code inside a single custom object, named dragObject. This object handles all event bindings, dragging operation, and cleanup. By encapsulating all properties and methods inside the object, the code keeps those names out of the global identifier space, thus minimizing potential conflicts with other scripts and element IDs.

The dragging system implemented in this example has a simple design behind it. Three mouse eventsmousedown, mousemove, and mouseupcontrol the action. Only the mousedown event is assigned at first; the others are bound to the element being dragged, and then released after the user drops the element. Initial event binding occurs in the dragObject.init( ) method. After grabbing a collection of all elements nested inside the body, the method assigns the mousedown event type to each element whose className property contains draggable.

When a user clicks on one of the draggable elements, the dragObject.engage( ) method preserves a reference to the element in the dragObject.selectedObject property (other methods will refer to it). In some browsers, if the draggable element contains other nodes, the event target thinks it's the nested node, rather than the draggable container. In that case, the code here works its way out from the target to the draggable container to make sure the desired element is registered as being draggable. Other jobs for the dragObject.engage( ) method include:

  • Raising the CSS z-index property so that the element is in front of all others. If your elements have specific z-index values, you could also preserve the original setting as another dragObject property for restoration after dragging.

  • Calculating the pixel offset within the container where the mousedown event occurred. This is necessary to allow the dragging to position the dragged element up and to the left of the cursor accurately so that the element doesn't jump to position its top-left corner under the cursor. W3C DOM and IE browsers calculate these coordinates differently, with IE requiring consideration of scrolling of the current page.

  • Binding temporary mousemove and mouseup events to the document node (via the "private" dragObject.setDragEvents( ) method). This allows events that might slip out of the element (e.g., the user scoots the mouse too fast for the dragging to catch up) to be processed as if they were inside the dragged element.

  • Preventing the mousedown event from propagating any further or returning any values. Unless these actions are cancelled, Macintosh users, in particular, see contextual menus if they press and hold the mouse button in a browser window.

As the user moves the mouse (with the button still pressed), the dragObject.dragIt( ) method runs repeatedly. It invokes the DHTMLAPI.moveTo( ) method (from the DHTMLapi3.js library) to keep the element positioned under the cursor. Because the target of the event might not be the container you wish to be dragged, the method refers to the dragObject.selectedObject property to obtain the reference of the real draggable element.

The instant the user releases the mouse button, the dragObject.releaseDrag( ) method performs three operations:

  • Restoring the CSS z-index property to zero (or saved value, if you wish to implement it that way).

  • Removing the mousemove and mouseup event bindings from the just-dragged element.

  • Setting dragObject.selectedObject to null because no element is currently being dragged.

Draggable elements in Example VI-3 consist of two images and one div element containing nothing more than some text. The code works with any rendered element.

Example VI-3. Dragging elements around the window

 <html> <head> <title>It's a Drag</title> <style type="text/css"> .draggable {position:absolute} #textbox {left: 200px; top: 225px; width: 150px;     height: 50px; border: solid black 1px;     background-color: lime; z-index: 0; text-align: center} #imgA {left: 50px; top: 200px; width: 120px; height: 90px;        border: solid black 1px; z-index: 0} #imgB {left: 110px; top: 245px; width: 120px; height: 90px;        border: solid black 1px; z-index: 0} </style> <script type="text/javascript" src="/books/2/570/1/html/2/eventsManager.js"></script> <script type="text/javascript" src="/books/2/570/1/html/2/DHTML3api.js"></script> <script type="text/javascript"> // dragObject contains data for currently dragged element var dragObject = {     selectedObject : null,     offsetX : 0,     offsetY : 0,     // invoked onmousedown     engageDrag : function(evt) {         evt = (evt) ? evt : window.event;         dragObject.selectedObject = (evt.target) ? evt.target : evt.srcElement;         var target = (evt.target) ? evt.target : evt.srcElement;         var dragContainer = target;         // in case event target is nested in draggable container         while (target.className != "draggable" && target.parentNode) {             target = dragContainer = target.parentNode;         }         if (dragContainer) {             dragObject.selectedObject = dragContainer;             DHTMLAPI.setZIndex(dragContainer, 100);             dragObject.setOffsets(evt, dragContainer);             dragObject.setDragEvents( );             evt.cancelBubble = true;             evt.returnValue = false;             if (evt.stopPropagation) {                 evt.stopPropagation( );                 evt.preventDefault( );             }         }         return false;     },     // calculate offset of mousedown within draggable element     setOffsets : function (evt, dragContainer) {         if (evt.pageX) {             dragObject.offsetX = evt.pageX - ((dragContainer.offsetLeft) ?                       dragContainer.offsetLeft : dragContainer.left);             dragObject.offsetY = evt.pageY - ((dragContainer.offsetTop) ?                       dragContainer.offsetTop : dragContainer.top);         } else if (evt.offsetX || evt.offsetY) {             dragObject.offsetX = evt.offsetX - ((evt.offsetX < -2) ?                       0 : document.body.scrollLeft);             dragObject.offsetY = evt.offsetY - ((evt.offsetY < -2) ?                       0 : document.body.scrollTop);         }     },     // invoked onmousemove     dragIt : function (evt) {         evt = (evt) ? evt : window.event;         var obj = dragObject;         if (evt.pageX) {             DHTMLAPI.moveTo(obj.selectedObject, (evt.pageX - obj.offsetX),                 (evt.pageY - obj.offsetY));         } else if (evt.clientX || evt.clientY) {             DHTMLAPI.moveTo(obj.selectedObject, (evt.clientX - obj.offsetX),                 (evt.clientY - obj.offsetY));         }         evt.cancelBubble = true;         evt.returnValue = false;     },     // invoked onmouseup     releaseDrag : function (evt) {         DHTMLAPI.setZIndex(dragObject.selectedObject, 0);         dragObject.clearDragEvents( );         dragObject.selectedObject = null;     },     // set temporary events     setDragEvents : function ( ) {         addEvent(document, "mousemove", dragObject.dragIt, false);         addEvent(document, "mouseup", dragObject.releaseDrag, false);     },     // remove temporary events     clearDragEvents : function ( ) {         removeEvent(document, "mousemove", dragObject.dragIt, false);         removeEvent(document, "mouseup", dragObject.releaseDrag, false);     },     // initialize, assigning mousedown events to all     // elements with  attributes     init : function (tagName) {         var elems = [];         if (document.all) {             // IE 5 & 5.5 don't know wildcard for getElementsByTagName             // so use document.body.all, which lets IE 4 work OK             elems = document.body.all;         } else if (document.body && document.body.getElementsByTagName) {             elems = document.body.getElementsByTagName("*");         }         for (var i = 0; i < elems.length; i++) {             if (elems[i].className.match(/draggable/)) {                 addEvent(elems[i], "mousedown", dragObject.engageDrag, false);             }         }     } }; // set onload event via eventsManager.js addOnLoadEvent(dragObject.init); </script> </head> <body> <h1>Element Dragging</h1> <hr> <div  >This is a draggable text box."</div> <img  name="imgA"  alt="draggable image A" src="/books/2/570/1/html/2/myImage1.gif" width="120" height="90" border="0" /> <img  name="imgB"   alt="draggable image B" src="/books/2/570/1/html/2/myImage4.gif" width="120" height="90" border="0"/> </body> </html> 




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