- Legend to Symbols

-
Interface ovi.mapsapi.dom.DragEventTarget
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 thedragstart 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 adragenter 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 propertylift 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>
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.
| {ovi.mapsapi.dom.DragEvent} | evt | The event object of this event. |
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.
| {ovi.mapsapi.dom.DragEvent} | evt | The event object of this event. |
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.
| {ovi.mapsapi.dom.DragEvent} | evt | The event object of this event. |
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.
| {ovi.mapsapi.dom.DragEvent} | evt | The event object of this event. |
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.
| {ovi.mapsapi.dom.DragEvent} | evt | The event object of this event. |
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.
| {ovi.mapsapi.dom.DragEvent} | evt | The event object of this event. |
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.
| {ovi.mapsapi.dom.DragEvent} | evt | The event object of this event. |