This section defines an event-based drag-and-drop mechanism.
This specification does not define exactly what a drag-and-drop operation actually is.
On a visual medium with a pointing device, a drag operation could be the
default action of a mousedown event
that is followed by a series of mousemove events, and the drop could be
triggered by the mouse being released.
On media without a pointing device, the user would probably have to explicitly indicate his intention to perform a drag-and-drop operation, stating what he wishes to drag and what he wishes to drop, respectively.
However it is implemented, drag-and-drop operations must have a starting point (e.g. where the mouse was clicked, or the start of the selection or element that was selected for the drag), may have any number of intermediate steps (elements that the mouse moves over during a drag, or elements that the user picks as possible drop points as he cycles through possibilities), and must either have an end point (the element above which the mouse button was released, or the element that was finally selected), or be canceled. The end point must be the last element selected as a possible drop point before the drop occurs (so if the operation is not canceled, there must be at least one element in the middle step).
DragEvent and DataTransfer interfacesThe drag-and-drop processing model involves several events. They all use
the DragEvent interface.
interface DragEvent : UIEvent {
readonly attribute DataTransfer dataTransfer;
void initDragEvent(in DOMString typeArg, in boolean canBubbleArg, in boolean cancelableArg, in AbstractView viewArg, in long detailArg, in DataTransfer dataTransferArg);
void initDragEventNS(in DOMString namespaceURIArg, in DOMString typeArg, in boolean canBubbleArg, in boolean cancelableArg, in AbstractView viewArg, in long detailArg, in DataTransfer dataTransferArg);
};
We should have modifier key information in here too (shift/ctrl, etc), like with mouse events and like with the context menu event.
The initDragEvent() and
initDragEventNS()
methods must initialise the event in a manner analogous to the
similarly-named methods in the DOM3 Events interfaces. [DOM3EVENTS]
The dataTransfer attribute
of the DragEvent interface
represents the context information for the event.
When a DragEvent object is
created, a new DataTransfer
object must be created and assigned to the dataTransfer context information field of
the event object.
interface DataTransfer {
attribute DOMString dropEffect;
attribute DOMString effectAllowed;
void clearData(in DOMString format);
void setData(in DOMString format, in DOMString data);
DOMString getData(in DOMString format);
void setDragImage(in Element image, in long x, in long y);
void addElement(in Element element);
};
DataTransfer objects can
conceptually contain various kinds of data.
When a DragEvent event object is
initialised, the DataTransfer
object created for the event's dataTransfer member must be initialised as
follows:
DataTransfer object must
initially contain no data, no elements, and have no associated image.
DataTransfer object's
effectAllowed attribute must be set to
"uninitialized".
dropEffect attribute must be set to "none".
The dropEffect attribute
controls the drag-and-drop feedback that the user is given during a
drag-and-drop operation.
The attribute must ignore any attempts to set it to a value other than
none, copy, link, and move. On getting, the
attribute must return the last of those four values that it was set to.
The effectAllowed
attribute is used in the drag-and-drop processing model to initialise the
dropEffect attribute during the dragenter and dragover events.
The attribute must ignore any attempts to set it to a value other than
none, copy, copyLink, copyMove, link, linkMove, move, all, and uninitialized. On getting, the attribute must return the
last of those values that it was set to.
DataTransfer objects can hold
pieces of data, each associated with a unique format. Formats are
generally given by MIME types, with some values special-cased for legacy
reasons.
The clearData(format) method must clear the DataTransfer object of any data
associated with the given format. If format is the value "Text", then it
must be treated as "text/plain". If the format is "URL", then it must be
treated as "text/uri-list".
The setData(format, data) method must
add data to the data stored in the DataTransfer object, labelled as being of
the type format. This must replace any previous data
that had been set for that format. If format is the
value "Text", then it must be treated as "text/plain". If the format is "URL", then it must be treated as "text/uri-list".
The getData(format) method must return the data that is
associated with the type format, if any, and must
return the empty string otherwise. If format is the
value "Text", then it must be treated as "text/plain". If the format is "URL", then the data associated with the "text/uri-list" format must be parsed as appropriate for
text/uri-list data, and the first URI from the list
must be returned. If there is no data with that format, or if there is but
it has no URIs, then the method must return the empty string. [RFC2483]
The setDragImage(element, x, y) method sets which element to use to generate the drag feedback. The element argument can be any Element; if it is
an img element, then the user agent should
use the element's image (at its intrinsic size) to generate the feedback,
otherwise the user agent should base the feedback on the given element
(but the exact mechanism for doing so is not specified).
The addElement(element) method is an alternative way of
specifying how the user agent is to render
the drag feedback. It adds an element to the DataTransfer object.
The following events are involved in the drag-and-drop model. Whenever
the processing model described below causes one of these events to be
fired, the event fired must use the DragEvent interface defined above, must have
the bubbling and cancelable behaviours given in the table below, and must
have the context information set up as described after the table, with the
view attribute set to the view with
which the user interacted to trigger the drag-and-drop event, and the
detail attribute set to zero.
| Event Name | Target | Bubbles? | Cancelable? | dataTransfer
| effectAllowed
| dropEffect
| Default Action |
|---|---|---|---|---|---|---|---|
dragstart
| Source node | ✓ Bubbles | ✓ Cancelable | Contains source node unless a selection is being dragged, in which case it is empty | uninitialized
| none
| Initiate the drag-and-drop operation |
drag
| Source node | ✓ Bubbles | ✓ Cancelable | Empty | Same as last event | none
| Continue the drag-and-drop operation |
dragenter
| Immediate user selection or the body element | ✓ Bubbles | ✓ Cancelable | Empty | Same as last event | Based on
effectAllowed value
| Reject immediate user selection as potential target element |
dragleave
| Previous target element | ✓ Bubbles | — | Empty | Same as last event | none
| None |
dragover
| Current target element | ✓ Bubbles | ✓ Cancelable | Empty | Same as last event | Based on
effectAllowed value
| Reset the current drag operation to "none" |
drop
| Current target element | ✓ Bubbles | ✓ Cancelable | getData() returns data set in dragstart event
| Same as last event | Current drag operation | Varies |
dragend
| Source node | ✓ Bubbles | — | Empty | Same as last event | Current drag operation | Varies |
The dataTransfer object's contents are empty
except for dragstart events and drop events, for which the
contents are set as described in the processing model, below.
The effectAllowed attribute must be set to
"uninitialized" for dragstart events, and to whatever value the
field had after the last drag-and-drop event was fired for all other
events (only counting events fired by the user agent for the purposes of
the drag-and-drop model described below).
The dropEffect attribute must be set to "none" for dragstart, drag, and dragleave events (except when stated
otherwise in the algorithms given in the sections below), to the value
corresponding to the current drag operation for
drop and dragend events, and to a
value based on the effectAllowed attribute's value and to
the drag-and-drop source, as given by the following table, for the
remaining events (dragenter and dragover):
effectAllowed
| dropEffect
|
|---|---|
none
| none
|
copy, copyLink, copyMove, all
| copy
|
link, linkMove
| link
|
move
| move
|
uninitialized when what is being dragged is a
selection from a text field
| move
|
uninitialized when what is being dragged is a
selection
| copy
|
uninitialized when what is being dragged is an
a element with an href
attribute
| link
|
| Any other case | copy
|
When the user attempts to begin a drag operation, the user agent must
first determine what is being dragged. If the drag operation was invoked
on a selection, then it is the selection that is being dragged. Otherwise,
it is the first element, going up the ancestor chain, starting at the node
that the user tried to drag, that has the DOM attribute draggable set to
true. If there is no such element, then nothing is being dragged, the
drag-and-drop operation is never started, and the user agent must not
continue with this algorithm.
img elements and a elements with an href attribute have their draggable attribute
set to true by default.
If the user agent determines that something can be dragged, a dragstart event must
then be fired.
If it is a selection that is being dragged, then this event must be fired on the node that the user started the drag on (typically the text node that the user originally clicked). If the user did not specify a particular node, for example if the user just told the user agent to begin a drag of "the selection", then the event must be fired on the deepest node that is a common ancestor of all parts of the selection.
We should look into how browsers do other types (e.g. Firefox apparently also adds text/html for internal drag and drop of a selection).
If it is not a selection that is being dragged, then the event must be fired on the element that is being dragged.
The node on which the event is fired is the source node. Multiple events are fired on this node during the course of the drag-and-drop operation.
If it is a selection that is being dragged, the dataTransfer member of the event must be
created with no nodes. Otherwise, it must be created containing just the
source node. Script can use the addElement() method to add further elements
to the list of what is being dragged.
If it is a selection that is being dragged, the dataTransfer member of the event must have
the text of the selection added to it as the data associated with the
text/plain format. Otherwise, if it is an img element being dragged, then the value of the
element's src DOM
attribute must be added, associated with the text/uri-list format. Otherwise, if it is an a element being dragged, then the value of the
element's href DOM
attribute must be added, associated with the text/uri-list format. Otherwise, no data is added to the
object by the user agent.
If the event is canceled, then the drag-and-drop operation must not occur; the user agent must not continue with this algorithm.
If it is not canceled, then the drag-and-drop operation must be initiated.
Since events with no event handlers registered are, almost by definition, never canceled, drag-and-drop is always available to the user if the author does not specifically prevent it.
The drag-and-drop feedback must be generated from the first of the following sources that is available:
setDragImage() method of the dataTransfer object of the dragstart event, if
the method was called. In visual media, if this is used, the x and y arguments that were passed to
that method should be used as hints for where to put the cursor relative
to the resulting image. The values are expressed as distances in CSS
pixels from the left side and from the top side of the image
respectively. [CSS21]dataTransfer object, both before the
event was fired, and during the handling of the event using the addElement() method, if any such elements
were indeed added.
The user agent must take a note of the data that was placed in the dataTransfer object. This data will be
made available again when the drop event is fired.
From this point until the end of the drag-and-drop operation, device input events (e.g. mouse and keyboard events) must be suppressed. In addition, the user agent must track all DOM changes made during the drag-and-drop operation, and add them to its undo history as one atomic operation once the drag-and-drop operation has ended.
During the drag operation, the element directly indicated by the user as the drop target is called the immediate user selection. (Only elements can be selected by the user; other nodes must not be made available as drop targets.) However, the immediate user selection is not necessarily the current target element, which is the element currently selected for the drop part of the drag-and-drop operation. The immediate user selection changes as the user selects different elements (either by pointing at them with a pointing device, or by selecting them in some other way). The current target element changes when the immediate user selection changes, based on the results of event handlers in the document, as described below.
Both the current target element and the immediate user selection can be null, which means no target element is selected. They can also both be elements in other (DOM-based) documents, or other (non-Web) programs altogether. (For example, a user could drag text to a word-processor.) The current target element is initially null.
In addition, there is also a current drag operation, which can take on the values "none", "copy", "link", and "move". Initially it has the value "none". It is updated by the user agent as described in the steps below.
User agents must, every 350ms (±200ms), perform the following steps in sequence. (If the user agent is still performing the previous iteration of the sequence when the next iteration becomes due, the user agent must not execute the overdue iteration, effectively "skipping missed frames" of the drag-and-drop operation.)
First, the user agent must fire a drag event at the source
node. If this event is canceled, the user agent must set the current drag operation to none (no drag operation).
Next, if the drag
event was not canceled and the user has not ended the drag-and-drop
operation, the user agent must check the state of the drag-and-drop
operation, as follows:
First, if the user is indicating a different immediate user selection than during the last iteration (or if this is the first iteration), and if this immediate user selection is not the same as the current target element, then the current target element must be updated, as follows:
If the new immediate user selection is null, or is in a non-DOM document or application, then set the current target element to the same value.
Otherwise, the user agent must fire a dragenter
event at the immediate user selection.
If the event is canceled, then the current target element must be set to the immediate user selection.
Otherwise, if the current target element
is not the body element, the user agent
must fire a dragenter event at the body element, and the current target element must be set to the body element, regardless of whether that
event was canceled or not. (If the body
element is null, then the current target
element would be set to null too in this case, it wouldn't be
set to the Document object.)
If the previous step caused the current target
element to change, and if the previous target element was not null
or a part of a non-DOM document, the user agent must fire a dragleave event
at the previous target element.
If the current target element is a DOM
element, the user agent must fire a dragover event at this current target element.
If the dragover event is canceled, the current drag operation must be reset to "none".
Otherwise, the current drag operation must
be set based on the values the effectAllowed and dropEffect attributes of the dataTransfer object had after the
event was handled, as per the following table:
effectAllowed
| dropEffect
| Drag operation |
|---|---|---|
uninitialized, copy,
copyLink, copyMove, or
all
| copy
| "copy" |
uninitialized, link,
copyLink, linkMove, or
all
| link
| "link" |
uninitialized, move,
copyMove, linkMove, or
all
| move
| "move" |
| Any other case | "none" | |
Then, regardless of whether the dragover event was canceled or not, the
drag feedback (e.g. the mouse cursor) must be updated to match the current drag operation, as follows:
| Drag operation | Feedback |
|---|---|
| "copy" | Data will be copied if dropped here. |
| "link" | Data will be linked if dropped here. |
| "move" | Data will be moved if dropped here. |
| "none" | No operation allowed, dropping here will cancel the drag-and-drop operation. |
Otherwise, if the current target element is not a DOM element, the user agent must use platform-specific mechanisms to determine what drag operation is being performed (none, copy, link, or move). This sets the current drag operation.
Otherwise, if the user ended the drag-and-drop operation (e.g. by
releasing the mouse button in a mouse-driven drag-and-drop interface),
or if the drag event
was canceled, then this will be the last iteration. The user agent must
execute the following steps, then stop looping.
If the current drag operation is none (no
drag operation), or, if the user ended the drag-and-drop operation by
canceling it (e.g. by hitting the Escape key), or if the current target element is null, then the drag
operation failed. If the current target
element is a DOM element, the user agent must fire a dragleave event
at it; otherwise, if it is not null, it must use platform-specific
conventions for drag cancellation.
Otherwise, the drag operation was as success. If the current target element is a DOM element, the user
agent must fire a drop event at it; otherwise, it must use
platform-specific conventions for indicating a drop.
When the target is a DOM element, the dropEffect attribute of the event's
dataTransfer object must be given the
value representing the current drag operation
(copy, link, or move), and the object must be set up so that the getData()
method will return the data that was added during the dragstart event.
If the event is canceled, the current drag
operation must be set to the value of the dropEffect attribute of the event's
dataTransfer object as it stood after
the event was handled.
Otherwise, the event is not canceled, and the user agent must perform the event's default action, which depends on the exact target as follows:
textarea, or an input element
with type="text")
text/plain format, if any, into the text field in a
manner consistent with platform-specific conventions (e.g. inserting
it at the current mouse cursor position, or inserting it at the end
of the field).
Finally, the user agent must fire a dragend event at the source node, with the dropEffect attribute of the event's
dataTransfer object being set to the
value corresponding to the current drag
operation.
The current drag operation can
change during the processing of the drop event, if one was fired.
The event is not cancelable. After the event has been handled, the user agent must act as follows:
textarea, or an input element
with type="text"), and a drop event was fired in
the previous step, and the current drag
operation is "move", and the source of the drag-and-drop
operation is a selection in the DOM
textarea, or an input element
with type="text"), and a drop event was fired in
the previous step, and the current drag
operation is "move", and the source of the drag-and-drop
operation is a selection in a text field
The model described above is independent of which Document
object the nodes involved are from; the events must be fired as described
above and the rest of the processing model must be followed as described
above, irrespective of how many documents are involved in the operation.
If the drag is initiated in another application, the source node is not a DOM node, and the user agent must
use platform-specific conventions instead when the requirements above
involve the source node. User agents in this situation must act as if the
dragged data had been added to the DataTransfer object when the drag
started, even though no dragstart event was actually fired; user
agents must similarly use platform-specific conventions when deciding on
what drag feedback to use.
If a drag is started in a document but ends in another application, then the user agent must instead replace the parts of the processing model relating to handling the target according to platform-specific conventions.
In any case, scripts running in the context of the document must not be able to distinguish the case of a drag-and-drop operation being started or ended in another application from the case of a drag-and-drop operation being started or ended in another document from another domain.
draggable attributeAll elements may have the draggable content attribute set. The draggable attribute
is an enumerated attribute. It has three states.
The first state is true and it has the keyword true. The second state is false and it has the
keyword false. The third state is auto; it
has no keywords but it is the missing value default.
The draggable
DOM attribute, whose value depends on the content attribute's in the way
described below, controls whether or not the element is draggable.
Generally, only text selections are draggable, but elements whose draggable DOM
attribute is true become draggable as well.
If an element's draggable content attribute has the state
true, the draggable DOM attribute must return true.
Otherwise, if the element's draggable content attribute has the state
false, the draggable DOM attribute must return false.
Otherwise, the element's draggable content attribute has the state
auto. If the element is an img
element, or, if the element is an a element
with an href
content attribute, the draggable DOM attribute must return true.
Otherwise, the draggable DOM must return false.
If the draggable DOM attribute is set to the value
false, the draggable content attribute must be set to
the literal value false. If the draggable DOM
attribute is set to the value true, the draggable content attribute must be set to
the literal value true.
Copy-and-paste is a form of drag-and-drop: the "copy" part is equivalent to dragging content to another application (the "clipboard"), and the "paste" part is equivalent to dragging content from another application.
Select-and-paste (a model used by mouse operations in the X Window System) is equivalent to a drag-and-drop operation where the source is the selection.
When the user invokes a copy operation, the user agent must act as if the user had invoked a drag on the current selection. If the drag-and-drop operation initiates, then the user agent must act as if the user had indicated (as the immediate user selection) a hypothetical application representing the clipbroad. Then, the user agent must act as if the user had ended the drag-and-drop operation without canceling it. If the drag-and-drop operation didn't get canceled, the user agent should then follow the relevant platform-specific conventions for copy operations (e.g. updating the clipboard).
When the user invokes a cut operation, the user agent must act as if the user had invoked a copy operation (see the previous section), followed, if the copy was completed successfully, by a selection delete operation.
When the user invokes a clipboard paste operation, the user agent must
act as if the user had invoked a drag on a hypothetical application
representing the clipboard, setting the data associated with the drag as
the text from the keyboard (either as text/plain or
text/uri-list). If the contents of the clipboard cannot be
represented as text or URIs, then the paste operation must not have any
effect.
Then, the user agent must act as if the user had indicated (as the immediate user selection) the element with the keyboard focus, and then ended the drag-and-drop operation without canceling it.
When the user invokes a selection paste operation, the user agent must act as if the user had invoked a drag on the current selection, then indicated (as the immediate user selection) the element with the keyboard focus, and then ended the drag-and-drop operation without canceling it.
If the contents of the selection cannot be represented as text or URIs, then the paste operation must not have any effect.
User agents must not make the data added to the DataTransfer object during the dragstart event
available to scripts until the drop event, because otherwise, if a user were to
drag sensitive information from one document to a second document,
crossing a hostile third document in the process, the hostile document
could intercept the data.
For the same reason, user agents must only consider a drop to be
successful if the user specifically ended the drag operation — if
any scripts end the drag operation, it must be considered unsuccessful
(canceled) and the drop
event must not be fired.
User agents should take care to not start drag-and-drop operations in response to script actions. For example, in a mouse-and-window environment, if a script moves a window while the user has his mouse button depressed, the UA would not consider that to start a drag. This is important because otherwise UAs could cause data to be dragged from sensitive sources and dropped into hostile documents without the user's consent.