Ovi Maps API Reference

Contents

Interface ovi.mapsapi.dom.DragEventTarget

Interface Summary

This class is a virtual interface that does only exist for documentational purpose. Each class implementing this interface will delcare that it is able to be the target of certain events. Each event in this interface (and therefore as well within the class implementing that interface) describes an event of a specific type.

The following example will show event handling at the example of an event of the type click:

// Note that "obj" can be either a DOM node or any other JavaScript object.
var obj = ovi.mapsapi.dom.EventTarget( {} );
obj.addListener("click", function(evt) {
	console.log("This is the '"+evt.type+"' event!");
});
obj.dispatch( new ovi.mapsapi.dom.Event({
	type: "click"
});
As you can see the event click doesn't mean that there is a method click that is called, but rather that an event of the type click is fired and dispatched to all listeners registered with the object that is the target of the event.

For more information about event dispatching please read the documentation of the ovi.mapsapi.dom.EventTarget class!

Drag and drop in a nutshell

The following description of the event handling will be done for the drag and drop events, which are little more complicated, but once understood it will become easy in the handling. You can find a very detailed explanation of the drag and drop at the W3C homepage, see http://dev.w3.org/html5/spec/dnd.html. However, this small help will give you the necessary information in a nutshell, plus give you some information about the none standard not lifted dragging which is an arbitrary extension to the W3C specification and will help you in using the drag and drop events for certain special cases (like to implement e.g. sliders).

The drag and drop persists out of seven events, three of them (dragstart, drag and dragend) being fired at the DOM node that is being dragged and four of them (dragenter, dragover, dragleave and drop) are fired at the DOM node where the source node is dragged above or dropped into (this whill not happen in the not lifted dragging, but more about that later).

Before we start

Before we begin with out small tutorial note that the following code is always expected to be executed before the small code examples which you'll find below:

 // Create a few shortcuts.
 var Page = ovi.mapsapi.dom.Page,
     EventTarget = ovi.mapsapi.dom.EventTarget;

 // Query page support for the document. This is very important, but needs only 
 // to be done once. However, doing it multiple times will not harm.
 Page(document);

Basic knowledge about dragging

First the DOM node that shall be dragged needs to become draggable, the ovi.mapsapi.dom.EventTarget class will support you here by offering a nice method that takes care of all cross browser specific specials:
 // Make a DOM node an event target and draggable.
 EventTarget(node).enableDrag();

That's it. Now the browser will fire a dragstart event as soon as the user tries to drag this node or any of it's child nodes, this is valid for touch screens too (like e.g. an iPhone). If the event is canceled, then the drag is disallowed, otherwise (so by default) dragging is allowed. While the whole drag operation is ongoing no other mouse, touch or keyboard events will be fired.

Note: The above mentioned as well applies to the mouseup and click events, therefore you'll receive a mousedown event, but no mouseup or click event will follow if this mousedown event started a drag operation, so if the dragstart event was not canceled (preventDefault was not called). Therefore it's highly recommented to not only register a listener to mousedown and mouseup if these two events shall be tracked, but as well to the dragend in the capturing phase, because if there was a mousedown that is followed by an dragend, then this means that the mouse button has been released, but you'll not receive a mouseup, therefore simply use the dragend as an mouseup event. If you recieved a mousedown at a node, you can be sure that you'll receive as well a dragend at the same node at least within the capturing phase.

Each of the seven drag events will have a special property called dataTransfer. This property is an instance of ovi.mapsapi.dom.DataTransfer and shall be used to control the drag operation and contains information about the drag operation. It can be used as well to transfer data between the different drag listeners as it is guaranteed that the object itself will be attached to all drag events following the dragstart event till the dragend event. However, the next drag sequence will have a new dataTransfer object attached (so you can't transfer data between to separate drag sequences).

The drag from the drag target perspective

Within the dragstart event the eventhandler shall set the effectAllowed property to one of the following strings: uninitialized, none, copy, copyLink, copyMove, all, link, linkMove or move. The default value will be uninitialized and has the same meaning as all. The allowed effect will decide what kind of operation is allowed with the dragged object, so if the object being dragged may be copied, linked and/or moved.

Within the dragstart event the visual feedback of the drag shall be defined as well. There are two methods available at the data transfer object to do that: setDragImage( node, offsetX, offsetY) and addElement( node ). You can only have one drag image, so a second call to setDragImage will overwrite the previously set image node, while addElement will add an unlimited number of additional elements into the visual feedback. The visual feedback is rendered with the top-left corner where the hot spot of the mouse cursor currently is or where the touch point of a finger at a touch screen is. All added elements and the drag image are rendered relative to this position. The offset of a drag image can be used to ensure that the image is shown correctly, e.g. where it has been touched (targetX, targetY). The offset has no effect for the elements added using the addElement method, but these elements may have the style position set to absolute and then they can be offset using the style.left and style.top CSS properties.

While the drag is ongoing every few milliseconds the brower will fire a "drag" event at the source node that is being dragged. If the event is canceled (so preventDefault is called), the drag is aborted, otherwise the drag will continue. In the most cases the drag event can be ignored, except e.g. for not lifted dragging, but that's explained later.

After the drag is finished the source node (the drag target) will receive a dragend event with the information what happend. This information is stored in the dropEffect property of the data transfer object and will be none, copy, move or link and reflects the action that happend to the dragged element at the drop zone. If the drop was aborted or failed the effect will be none. For our first example this will be an important information, because if the dragged element was not moved, the node must be re-inserted into the document to the old position where it was before the drag started. The following example shows this:

 // This function will make an image node draggable.
 var makeImageDraggable = function ( node ) {
   // Create a closure so that every call to this method 
   // can handle it's own node.
   (function(node){
     // Make the image draggable.
     EventTarget(node).enableDrag();
 
     // These variables will be used later to put the image
     // back to where it was, it the drag failed.
     var sourceNodeParent;
     var sourceNodeNextSibling;

     // Register a dragstart listener.
     node.addListener("dragstart", function(evt) {
       // Remember the parent node and next sibling of the image.
       sourceNodeParent = node.parentNode;
       sourceNodeNextSilbing = sourceNode.nextSilbing;
       
       // Detach the image from the document.
       sourceNodeParent.removeChild (node);
       
       // Attach the image to the cursor so that it is dragged around.
       evt.dataTransfer.setDragImage( sourceNode, -evt.targetX || 0, -evt.targetY || 0);
       
       // Allow all kind of drag operations.
       evt.dataTransfer.effectAllowed = "all";
       
       // Prevent that any other drag handler receives this event,
       // but we must not cancel the event, because otherwise the 
       // dragging will be aborted.
       evt.stopImmediatePropagation();
     }, false);
     
     // Register a dragend listener.
     node.addListener("dragend", function(evt) {
       // If the image was not moved somewhere else
       if (evt.dataTransfer.dropEffect!=="move") {
         // we have to move the image back to where it was.
         if (sourceNodeNextSilbing)
           sourceNodeParent.insertBefore(node, sourceNodeNextSilbing);
         else
           sourceNodeParent.appendChild(node);
       }
       // Note: If the image was moved, we don't need to do anything!

       // Let's cancel the event as we've processed it and the browser
       // shall not do anything nor any other listener shall do something.
       event.cancel();
     }, false);
   })(node);
 };
So far we've explained one part of the drag and drop events. This is what has to be done by the one who want's to make a DOM node draggable, but the second part of the job is to create a drop zone where something can be dropped. To extend our example we'll allow the drop of the image everywhere within the document.

The drag from the drop target perspective

A dropzone can be any DOM node or JavaScript object that is an ovi.mapsapi.dom.EventTarget. Whenever the mouse is moved above a node the browser will fire a dragenter event at that node. If the event is not canceled (the preventDefault method not called), the browser will estimate that a drop is not handled by the node and will deny the drop to that node. If the event is canceled (the preventDefault method was called) the listener must set the property dropEffect of the data transfer object to the operation that the node wants to perform with the dragged element, if it is dropped there. In our case of an image being dragged this would be move.

The next event being fired is the dragover event, which is fired regularly like the drag event, just that the dragover is fired at the current drop target. This event must be canceled (so preventDefault must be called) and the property dropEffect of the data transfer object shall be updated to the currently desired operation, so none, copy, link or move. If the event is not canceled the drop will be denied and the dropEffect is set to none.

Finally if the user releases the mouse button or finger (touch screen) above a drop target that has set the dropEffect to copy, link or move a drop event will be fired at the drop target. The drop handler now shall perform the set drop effect and cancel the event (call the preventDefault method). If the event is not canceled the browser shall automatically perform the drop action (which only works for very specific nodes).

The following example shows a complete drag and drop example for the image:

<html>
<head>
 <!-- please load the API here -->
 <script language="JavaScript">
   // NOTE: This is very important, otherwise no eventhandling at all is possible!
   var page = ovi.mapsapi.dom.Page(document);

   // Create a shortcut to the event target class.
   var EventTarget = ovi.mapsapi.dom.EventTarget;

   // This function will make an image node draggable.
   var makeImageDraggable = function ( node ) {
     // Create a closure so that every call to this method 
     // can handle it's own node.
     (function(node){
       // Make the image draggable.
       EventTarget(node).enableDrag();
 
       // These variables will be used later to put the image
       // back to where it was, it the drag failed.
       var sourceNodeParent;
       var sourceNodeNextSibling;

       // Register a dragstart listener.
       node.addListener("dragstart", function(evt) {
         // Remember the parent node and next sibling of the image.
         sourceNodeParent = node.parentNode;
         sourceNodeNextSilbing = node.nextSilbing;
       
         // Detach the image from the document
         sourceNodeParent.removeChild (node);
       
         // Attach the image to the cursor so that it is dragged around.
         evt.dataTransfer.setDragImage( node, -evt.targetX || 0, -evt.targetY || 0);
       
         // Allow all kind of drag operations.
         evt.dataTransfer.effectAllowed = "all";

         // Add the image node into the data transfer for the drop zone.
         evt.dataTransfer.setData("image/gif", node);

         // Prevent that any other drag handler to receive this event,
         // but we must not cancel the event, because otherwise the 
         // dragging will be aborted.
         evt.stopImmediatePropagation();
       }, false);
     
       // Register a dragend listener.
       node.addListener("dragend", function(evt) {
         // If the image was not moved somewhere else
         if (evt.dataTransfer.dropEffect!=="move") {
           // we have to move the image back to where it was.
           if (sourceNodeNextSilbing)
             sourceNodeParent.insertBefore(node, sourceNodeNextSilbing);
           else
             sourceNodeParent.appendChild(node);
         }
         
         // Note: If the image was moved, we don't need to do anything!

         // Let's cancel the event as we've processed it and the browser
         // shall not do anything nor any other listener shall do something.
         evt.cancel();
       }, false);
     })(node);
   };
   
   function initDnD() {
     // First of all make the image draggable.
     makeImageDraggable (document.getElementById("draggableImage") );
     
     // Get the DOM node for our drop zone.
     var dropZone = EventTarget(document.getElementById("dropzone"));
     
     // If the draggable image is dragged into our drop zone
     dropZone.addListener("dragenter", function(evt) {
       // Allow the movement here of all GIF images to here.
       if (evt.dataTransfer.hasData("image/gif")) {
         evt.dataTransfer.dropEffect = "move";
         evt.cancel();
       }
	    }, false);

     // While the draggable image is moved above the drop zone
     dropZone.addListener("dragover", function(evt) {
       // Allow the movement of all GIF images to here.
       if (evt.dataTransfer.hasData("image/gif")) {
         evt.dataTransfer.dropEffect = "move";
         evt.cancel();
       }
	    }, false);
     
     // If the draggable image is dropped into our drop zone
     dropZone.addListener("drop", function(evt) {
       // If the GIF image is dropped here, add it into our drop zone
       // and set the drop effect to "move".
       if (evt.dataTransfer.hasData("image/gif")) {
         dropZone.appendChild (evt.dataTransfer.getData("image/gif"));
         evt.dataTransfer.dropEffect = "move";
         evt.cancel();
       }
	    }, false);
   }
 </script>
</head>
<body onload="initDnD()">
 <div>
   <img id="draggableImage" src="http://www.w3.org/Icons/WWW/w3c_home_nb.gif" />
 </div>
 <div id="dropzone" style="width:400px; height:300px; background-color:#eee;">
   <p>Drop the image here</p>
 </div>
</body>
</html>

The not lifted drag and drop

The not lifted drag and drop is a simplified and optimized drag and drop. It is useful if you want to make an element draggable that is caged into a specific area and can't leave this area, like e.g. the knob of a scroll bar. Let's say you want to animate the knob of a scroll bar, but for sure you don't want to attach the knob to the mouse cursor so that the user can drag it around; you rather want to fix the knob within the scroll bar as if it is fixed with the bar. Anyway you want to be able to drag it. You can realize this by setting the property lift at the data transfer object of the dragstart event to false. This will cause three things:
* First the dragenter, dragover, dragleave and drop events will no longer be fired, therefore there will be no drop zone for the dragged element.
* Second the cursor being visible while dragging can be defined by the listener of the dragstart and drag event, ignoring the effectAllowed and dropEffect properties. This can be done by modifying the cursor property of the dataTransfer object.
* Third a not lifted drag is always a pure soft event, that means simulated. The native browser drag and drop events will not be used, therefore such an event can't leave the document and no other element or external application will notice this drag, what is the sense of the not lifted dragging.

Therefore not lifted dragging makes it very easy to implement sliders as the following example shows:
<html>
<head>
 <!-- please load API here -->
 <script language="JavaScript">
   // NOTE: This is very important, otherwise no eventhandling at all is possible!
   var page = ovi.mapsapi.dom.Page(document);

   // Create a shortcut to the event target class.
   var EventTarget = ovi.mapsapi.dom.EventTarget;
   
   function initDnD() {
     // Let's make the scrollbar and the knob draggable.
     var scrollbar = EventTarget(page.$("scrollbar") ).enableDrag();
     var knob = EventTarget(page.$("knob") ).enableDrag();

     // Attach a dragstart listener to the scrollbar.
     scrollbar.addListener("dragstart", function(evt) {
       // If the know is being dragged.
       if (evt.target===knob) {
         // Remember that the knob is dragged and set the value to the offset
         // of the dragstart relative to the top-left corner of the target.
         evt.dataTransfer.setData("application/slider", evt.targetY || 0);
         
         // Switch to not lifted dragging and make the cursor a hand.
         evt.dataTransfer.lift = false;
         evt.dataTransfer.cursor = "hand";
       } else
       // If the user tries to drag the scalebar, cancel the event and 
       // deny the drag
         evt.cancel();
		}, false);

     // Attach a drag listener to the scrollbar.
     scrollbar.addListener("drag", function(evt) {
       // If the know is dragged
       if (evt.dataTransfer.hasData("application/slider")) {
         var targetY = evt.dataTransfer.getData("application/slider");

         // Let's calculate the absolute position of the scrollbar
         // within the document.
         var scrollbarPos = page.getClientRect(scrollbar);
         
         // Let's calculate the position of the knob relative 
         // to the scrollbar, parallel to the mouse cursor.
         var y = evt.pageY - scrollbarPos.top - targetY;
         
         // If the knob would be moved outside of the scrollbar,
         // fix this.
         if (y<0) y = 0;
         if (y+knob.offsetHeight >= scrollbar.offsetHeight)
           y = scrollbar.offsetHeight - knob.offsetHeight - 1;
         
         // Move the knob.
         knob.style.top = y+"px";
       }
     }, false);
   }
 </script>
</head>
<body onload="initDnD()">
 <div id="scrollbar" style="position:absolute; top:10px; left:10px; width:30px; height: 200px; background-color:#eee; border:1px solid black;">
   <div id="knob" style="position:absolute; top:0px; left:3px; width:24px; height:40px; background-color:black;"> </div>
 </div>
</body>
</html>

Events
drag(evt)
Fired at the target object of the dragstart event while a drag operation is ongoing. This event will be fired every few milliseconds while the drag operation is ongoing. The relatedTarget property will contain the reference to the object that is currently below the mouse/finger (only for simulated drag events, not for the native onces).

Note: Please refer to the class documentation of the ovi.mapsapi.dom.DragEvent to get a much better and more detailed description of this and other drag events.

Parameters:
{ovi.mapsapi.dom.DragEvent} evt The event object of this event.
dragend(evt)
Fired at the end of a drag operation at the object that was dragged, so which was the target of the dragstart event. The event is fired if the drop was aborted, failed or successful. The event signalizes the end of a drag operation. The state of the drop operation can be read from the dropEffect which will either be "none", "move", "copy" or "link" and reflects what the drop target has performed. This information can be used to either remove the dragged object (for "move") or to do nothing (for the other drop effects).

Note: Please refer to the class documentation of the ovi.mapsapi.dom.DragEvent to get a much better and more detailed description of this and other drag events.

Parameters:
{ovi.mapsapi.dom.DragEvent} evt The event object of this event.
dragenter(evt)
Fired at an object if the mouse/finger is moved into the visible area of the object, while being within an drag operation. If the event is canceled (so the preventDefault method was called at the event object), a drop at the target is allowed, otherwise a drop is denied. If the drop is allowed, the target becomes the current drop target.

Warning: Despite it's name the behave of this event is rather like the one of mouseover then the one of mouseenter event!

Note: Please refer to the class documentation of the ovi.mapsapi.dom.DragEvent to get a much better and more detailed description of this and other drag events.

Parameters:
{ovi.mapsapi.dom.DragEvent} evt The event object of this event.
dragleave(evt)
Fired at the current drop target if the the mouse/finger leaves the visible area of the target. Note that despite it's name it's behave is like the one of mouseout rather then the one of mouseleave!

Warning: Despite it's name the behave of this event is rather like the one of mouseout then the one of mouseleave event!

Note: Please refer to the class documentation of the ovi.mapsapi.dom.DragEvent to get a much better and more detailed description of this and other drag events.

Parameters:
{ovi.mapsapi.dom.DragEvent} evt The event object of this event.
dragover(evt)
Fired at the current drop target while the mouse/finger is above the drop target. Note that the dragenter event must be canceled (preventDefault called) to make the object the current drop target. So if the dragenter event is not canceled, then no dragover events will be fired at all. This is event is fired every few milliseconds as long as the mouse cursor is above the drop target. If this event is not canceled (preventDefault) the dropEffect property is set to "none", what will disallow a drop into the drop target. If the event is canceled and the dropEffect is not "none", then a drop into this drop target is allowed.

Warning: Despite it's name the behave of this event is very different from the behave of the mouseover event!

Note: Please refer to the class documentation of the ovi.mapsapi.dom.DragEvent to get a much better and more detailed description of this and other drag events.

Parameters:
{ovi.mapsapi.dom.DragEvent} evt The event object of this event.
dragstart(evt)
Fired at an object that has the property draggable set to true and after a mousedown/touch happened and the mouse/finger was moved for at least 3 pixel. If the event is not canceled (so preventDefault of the event was not called), then the drag operation is permitted. The property dataTransfer can be used to keep track of information while a drag operation is ongoing.

Note: Please refer to the class documentation of the ovi.mapsapi.dom.DragEvent to get a much better and more detailed description of this and other drag events.

Parameters:
{ovi.mapsapi.dom.DragEvent} evt The event object of this event.
drop(evt)
Fired at the current drop target if the mouse button or finger is release above it, that means that the dragenter event and the dragover events where canceled, so their preventDefault methods where called and the allowedEffect property matches the dropEffect property.

If the event is not canceled (preventDefault called) and the drop target is a text field (e.g. textarea or input element), then the content of the "text/plain" format will be inserted into this area (where the cursor currently is or added to the end), otherwise the dropEffect is set to "none" and nothing will happen. If preventDefault was called the above described will not happen.

Note: Please refer to the class documentation of the ovi.mapsapi.dom.DragEvent to get a much better and more detailed description of this and other drag events.

Parameters:
{ovi.mapsapi.dom.DragEvent} evt The event object of this event.
Documentation generated on Fri Aug 19 2011 13:36:01 GMT+0200 (CEST).