Safari, Dashboard, and WebKit-based applications include support for customizing the behavior of drag and drop operations within your HTML pages.
Note: This technology is supported only on desktop versions of Safari. For iPhone OS, use DOM Touch, described in Handling Events (part of Safari Web Content Guide) and Safari DOM Additions Reference.
Support for Drag and Drop operations is implemented in JavaScript and may be applied to individual elements of your HTML page. For drag operations, an element can handle the following JavaScript events:
ondragstart
ondrag
ondragend
The ondragstart
event initiates the drag operation. You can provide a handler for this event to initiate or cancel drag operations selectively. To cancel a drag operation, call the preventDefault
method of the event object. To handle an event, assign a value to the effectAllowed
property and put the data for the drag in the dataTransfer
object, which you can get from the event object. See “Changing Drag Effects” for information on the effectAllowed
property. See “Manipulating Dragged Data” for information on handling the drag data.
Once a drag is under way, the ondrag
event is fired continuously at the element to give it a chance to perform any tasks it wants to while the drag is in progress. Upon completion of the operation, the element receives the ondragend
event. If the drag was successful, the ondrop
handler for the drop target element is also called (before the ondragend
handler is called).
While a drag is in progress, events are sent to elements that are potential drop targets for the contents being dragged. Those elements can handle the following events:
ondragenter
ondragover
ondragleave
ondrop
The ondragenter
and ondragleave
events let the element know when the user’s mouse enters or leaves the boundaries of the element. You can use these events to change the cursor or provide feedback as to whether a drop can occur on an element. The ondragover
event is sent continuously while the mouse is over the element to give it a chance to perform any needed tasks. If the user releases the mouse button, the element receives an ondrop
event, which gives it a chance to incorporate the dropped content.
If you implement handlers for the ondragenter
and ondragover
events, you should call the preventDefault
method of the event object. This method takes no parameters and notifies WebKit that your handler will act as the receiver of any incoming data. If you do not call this method, WebKit receives the data and incorporates it for you. You do not need to call preventDefault
if you simply want to be notified when the events occur.
Note: You must, at minimum, implement ondragover
and call the preventDefault
method on the event object. If you do not do this, you will not receive any of these four events.
You can add handlers for drag and drop events to any element in a web page. When a drag or drop operation occurs, WebKit looks for the appropriate handler on the element that is the focus of the operation. If that element does not define a handler, WebKit walks up the list of parent elements until it finds one that does. If no element defines a handler, WebKit applies the default behavior. To demonstrate this process, suppose you have the following basic HTML in a web page:
<body ondragstart="BodyDragHandler()" |
ondragend="BodyDragEndHandler()"> |
<span ondragstart="SpanDragHandler()">Drag this text.</span> |
</body> |
If a user initiates a drag operation on the text in the span
tag, WebKit calls SpanDragHandler
to handle the event. When the drag operation finishes, WebKit calls the BodyDragEndHandler
to handle the event.
WebKit provides automatic support to let users drag common items, such as images, links, and selected text. You can extend this support to include specific elements on an HTML page. For example, you could mark a particular div
or span
tag as draggable.
To mark an arbitrary element as draggable, add the -webkit-user-drag
attribute (previously -khtml-user-drag
) to the style definition of the element. Because it is a cascading style sheet (CSS) attribute, you can include it as part of a style definition or as an inline style attribute on the element tag. The values for this attribute are listed in Table 1.
The following example shows how you might use this attribute in a span
tag to permit the dragging of the entire tag. When the user clicks on the span
text, WebKit identifies the span as being draggable and initiates the drag operation.
<span style="color:rgb(22,255,22); -webkit-user-drag:element;">draggable text</span> |
When an event occurs, your handler uses the dataTransfer
object attached to the event to get and set the clipboard data. This object defines the clearData
, getData
, and setData
methods to allow you to clear, get, and set the data on the dragging pasteboard.
Note: For security purposes, the getData
method can be called only from within the ondrop
event handler.
Unlike many other browsers, the WebKit drag-and-drop implementation supports data types beyond those that are found in HTML documents. When you call either getData
or setData
, you specify the MIME type of the target data. For types it recognizes, WebKit maps the type to a known pasteboard type. However, you can also specify MIME types that correspond to any custom data formats your application understands. For most drag-and-drop operations, you will probably want to work with simple data types, such as plain text or a list of URIs.
Like applications, WebKit supports the ability to post the same data to the pasteboard in multiple formats. To add another format, you simply call the setData
method with a different MIME type and a string of data that conforms to that type.
To get a list of types currently available on the pasteboard, you can use the types
property of the dataTransfer
object. This property contains an array of strings with the MIME types of the available data.
When dragging content from one place to another, it might not always make sense to move that content permanently to the destination. You might want to copy the data or create a link between the source and destination documents instead. To handle these situations, you can use the effectAllowed
and dropEffect
properties of the dataTransfer
object to specify how you want data to be handled.
The effectAllowed
property tells WebKit what types of operation the source element supports. You would typically set this property in your ondragstart
event handler. The value for this property is a string, whose value can be one of those listed in Table 2.
The dropEffect
property specifies the single operation supported by the drop target (copy
, move
, link
, or none
). When an element receives an ondragenter
event, you should set the value of this property to one of those values, preferably one that is also listed in the effectAllowed
property. If you do not specify a value for this property, WebKit chooses one based on the available operations (as specified in effectAllowed
). Copy operations have priority over move operations, which have priority over link operations.
When these properties are set by the source and target elements, WebKit displays feedback to the user about what type of operation will occur if the dragged element is dropped. For example, if the dragged element supports all operations but the drop target only supports copy operations, WebKit displays feedback indicating a copy operation would occur.
During a drag operation, WebKit provides feedback to the user by displaying an image of the dragged content under the mouse. The default image used by WebKit is a snapshot of the element being dragged, but you can change this image to suit your needs.
The simplest way to change the drag-image appearance is to use cascading style sheet entries for draggable elements. WebKit defines the -webkit-drag
(formerly -khtml-drag
) pseudoclass, which you can use to modify the style definitions for a particular class during a drag operation. To use this pseudoclass, create a new empty style-sheet class entry with the name of the class you want to modify, followed by a a colon and the string -webkit-drag
. In the style definition of this new class, change or add attributes to specify the differences in appearance between the original element and the element while it is being dragged.
The following example shows the style-sheet definition for an element. During normal display, the appearance of the element is determined by the style-sheet definition of the divSrc4
class. When the element is dragged, WebKit changes the background color to match the color specified in the divSrc4:-webkit-drag
pseudoclass.
#divSrc4 { |
display:inline-block; |
margin:6; |
position:relative; |
top:20px; |
width:100px; |
height:50px; |
background-color:rgb(202,232,255); |
} |
#divSrc4:-webkit-drag { |
background-color:rgb(255,255,154) |
} |
Another way to change the drag image for an element is to specify a custom image. When a drag operation begins, you can use the setDragImage
method of the dataTransfer
object. This method has the following definition:
function setDragImage(image, x, y) |
The image
parameter can contain either a JavaScript Image object or another element. If you specify an Image object, WebKit uses that image as the drag image for the element. If you specify an element, WebKit takes a snapshot of the element you specify (including its child elements) and uses that snapshot as the drag image instead.
The x
and y
parameters of setDragImage
specify the point of the image that should be placed directly under the mouse. This value is typically the location of the mouse click that initiated the drag, with respect to the upper-left corner of the element being manipulated.
Unfortunately, obtaining this information in a cross-browser fashion is easier said than done. There is no standard way to determine the position of the mouse relative to the document because different browsers implement the standard event values in subtly incompatible ways.
For the purposes of Safari and WebKit, clientX
and clientY
are document relative, as are pageX
and pageY
(which are thus always equal to clientX
and clientY
). For other browsers, Evolt.org has an article that describes how to obtain the mouse position in a cross-browser fashion, including sample code, at http://evolt.org/article/Mission_Impossible_mouse_position/17/23335/index.html.
Obtaining the position of the element under the mouse is somewhat easier. QuirksMode has a page (with code samples) on the subject at http://www.quirksmode.org/js/findpos.html.
The drag-and-drop functionality built into WebKit and Safari works similarly to support in Microsoft Internet Explorer. The functionality built into FireFox and other Gecko-based browsers is very different, however.
In currently released versions of FireFox, this form of drag and drop is not generally supported from ordinary webpages (signed XUL applications notwithstanding) because you cannot register a drop target without loading an XPCOM component. Thus, with the exception of dropping things onto text areas (which are already drop targets), drag and drop must be emulated on this browser using mouse event handlers such as onmouseup
.
As a result of differences in browser support, most web developers who need drag-and-drop support use libraries that mask browser differences. Some of these include the Dojo Toolkit, DOM-Drag, ToolMan, Rico, and others.
No description of drag and drop would be complete without a working example. Save this into an HTML file and open it in Safari. You should see a very simple set of boxes containing words. If you drag each word box into the blue “target” box, the box will disappear and the word will appear in its correct place to form the phrase “This is a test”.
<html> |
<head> |
<script language="javascript" type="text/javascript"><!-- |
var dragitem = undefined; |
function setdragitem(item, evt) { |
dragitem=item; |
// alert('item: '+item); |
// item is an HTML DIV element. |
// evt is an event. |
// If the item should not be draggable, enable this next line. |
// evt.preventDefault(); |
return true; |
} |
function cleardragitem() { |
dragitem=undefined; |
// alert('item: '+item); |
} |
function dodrag() { |
// alert('item: '+dragitem); |
} |
// This is required---used to tell WebKit that the drag should |
// be allowed. |
function handledragenter(elt, evt) { |
evt.preventDefault(); |
return true; |
} |
function handledragover(elt, evt) { |
evt.preventDefault(); |
return true; |
} |
function handledragleave(elt, evt) { |
} |
function handledrop(elt, evt) { |
// alert('drop'); |
dragitem.style.display="none"; |
var newid=dragitem.id + '_dest'; |
var dest = document.getElementById(newid); |
dest.innerHTML = dragitem.innerHTML; |
} |
// --></script> |
<style type="text/css"><!-- |
.wordbox { border: 1px solid black; text-align: center; width: 50px; float: left; -webkit-user-drag: element; -webkit-user-select: none; } |
.spacer { clear: both; } |
.target { margin-top: 30px; padding: 30px; width: 70px; border: 1px solid black; background: #c0c0ff; margin-bottom: 30px; -webkit-user-drop: element; } |
.word { margin: 30px; min-height: 30px; border-bottom: 1px solid black; width: 50px; float: left; } |
--></style> |
</head> |
<body> |
<p>Drop words onto target area to put them in their places.</p> |
<div class='wordbox' id='this' ondragstart='setdragitem(this, event);' ondrag='dodrag();' ondragend='cleardragitem();'>This</div> |
<div class='wordbox' id='is' ondragstart='setdragitem(this, event);' ondrag='dodrag();' ondragend='cleardragitem();'>is</div> |
<div class='wordbox' id='a' ondragstart='setdragitem(this, event);' ondrag='dodrag();' ondragend='cleardragitem();'>a</div> |
<div class='wordbox' id='test' ondragstart='setdragitem(this, event);' ondrag='dodrag();' ondragend='cleardragitem();'>test</div> |
<div class='spacer'></div> |
<div class='target' ondragenter='handledragenter(this, event);' ondragover='handledragover(this, event);' ondragleave='handledragleave(this, event);' ondrop='handledrop(this, event);'>TARGET</div> |
<div class='words'> |
<div class='word' id='this_dest'></div> |
<div class='word' id='is_dest'></div> |
<div class='word' id='a_dest'></div> |
<div class='word' id='test_dest'></div> |
</div> |
</body> |
</html> |
Last updated: 2010-02-01