Drag-and-drop is a way to convert the pointing device's movements and clicks to special commands that are recognized by software to provide quick access to common functions of a program. The user grabs a virtual object, drags it to a different location or another virtual object, and drops it there. Drag-and-drop support for a native browser means faster, more responsive, and more intuitive web applications. Before you use the drag-and-drop feature, make sure you have draggable content.
An abstract Element
class has a draggable
attribute that indicates whether the element can be dragged and dropped. As all the DOM elements emerge from the Element
abstract class, this means all of them support the drag-and-drop operation by default. To make elements draggable, we need to set their draggable
attribute to true
. This can be done using the following code:
var dragSource = querySelector("#sample_drag_id"); dnd.draggable = true;
Alternatively, you can do this using the following HTML markup:
<p id="sample_dnd_id" draggable="true">Drag me!</p>
If you want to prevent the text contents of draggable elements from being selected, you can style the element, as shown in the following code:
[draggable] { -moz-user-select: none; -khtml-user-select: none; -webkit-user-select: none; user-select: none; -khtml-user-drag: element; -webkit-user-drag: element; }
Let's open the drag_and_drop
project and run it. In the following screenshot, you will see that you can drag the text element within the window of the browser but cannot drop it:
To manage the drag-and-drop operations in the example, add the drag-and-drop event listeners described in the next section.
The native drag-and-drop API fires the following events:
dragstart
: This event is fired on an element when a drag starts. Information such as the drag data and image to be associated with the drag operation can be set in the listener.dragenter
: This event is fired when the cursor is hovered over an element for the first time while a drag begins. A drop operation is not allowed by default. There are one or more listeners that perform drag-and-drop operations. Usually, the listener highlights or marks the drop element where the drop can occur.dragover
: This event is fired when the cursor is hovered over an element and a drag is in process.dragleave
: This event is fired when the cursor leaves an element while a drag is in process. The listener will remove highlights or markers from the element where the drop can occur.drag
: This event is fired on an element where the dragstart
event was fired.drop
: This event is fired on an element where the drop occurred. It is fired only if the drop is allowed. Users can cancel the drag operation by pressing the Esc key or releasing the mouse button on an invalid drop area.dragend
: This event is fired on an element on which the drag was started to inform that the drag operation is complete, regardless of whether it is successful or not.We will continue to make elements draggable from our example. We need to add a listener for the dartstart
event and set the drag data within the listener, as follows:
var dragSource = querySelector("#sample_drag_id") ..draggable = true ..onDragStart.listen((MouseEvent event) { //… });
If an element is made draggable, you cannot select the text by clicking-and-dragging with the mouse.
Each drag event has a dataTransfer
property that is used to hold data associated with the drag operation. If you drag the selected text, then the associated data is text. If you drag an image, then the associated data is the image itself. The drag data combines the string representation of the format of the data and the data value. We will use the format of the data in the event listeners for the dragenter
and dragover
events to check whether the drop operation is allowed or not. You can set multiple drag data to call the setData
method multiple times with different formats. To delete them, call the clearData
method of the dataTransfer
property, as shown in the following code:
var dragSource = querySelector("#sample_dnd_id") ..draggable = true ..onDragStart.listen((MouseEvent event) { event.dataTransfer.setData("text/plain", "I'm draggable"); event.dataTransfer.setData("text/data", "1234"); });
Usually, native drag-and-drop APIs automatically create translucent images that are generated from the drag target of the dragstart
event, which follows the mouse pointer during the drag operation. You can use the setDragImage
method of the dataTransfer
property to specify a custom drag image. The first argument of this function is a custom drag image, which could be a reference to a real image, canvas, or other elements. The second and third arguments are offsets where the image should appear relative to the mouse pointer. The code is as follows:
var dragSource = querySelector("#sample_drag_id") ..draggable = true ..onDragStart.listen((MouseEvent event) { event.dataTransfer.setData("text/plain", "I'm draggable"); event.dataTransfer.setDragImage(new ImageElement(src:'notification.png'), 0, 0); });
The following feedback image will appear instead of the standard translucent image:
The drag-and-drop API supports operations such as copy, move, link, and their combinations that may be performed on data that is draggable. We can use the copy
operation to indicate that the data being dragged will be copied from its present location to the drop location. Similarly, you can use the move
operation to indicate that the data being dragged will be moved, and the link
operation indicates that connections will be created between the source and drop locations. You should specify which operation or combinations are allowed and are performed by setting the effectAllowed
property of the dragstart
event within a listener, as shown in the following code:
var dragSource = querySelector("#sample_drag_id") ..draggable = true ..onDragStart.listen((MouseEvent event) { event.dataTransfer.setData("text/plain", "I'm draggable"); event.dataTransfer.setDragImage(new ImageElement(src:'notification.png'), 0, 0); event.dataTransfer.effectAllowed = 'copy'; });
In the preceding example, we allowed only the copy
operation. Let's see all the values that we can use as the name for an operation:
none
: This operation means that no operation is permittedcopy
: This operation means that the drag data can only be copied from source to drop locationmove
: This operation means that the drag data can only be moved from source to drop locationlink
: This operation means that the drag data can only be linked from source to drop locationcopyMove
: This operation means that the drag data can be copied or moved from source to drop locationcopyLink
: This operation means that the drag data can be copied or linked from source to drop locationall
: This operation means that the drag data can be copied, moved, or linked from source to drop locationBy default, the effectAllowed
property allows all three operations. The permitted operation can be checked in a listener for the dragenter
or dragover
events via the effectAllowed
property, and it should be set in a related dropEffect
property to specify which single operation should be performed. The valid operations for the dropEffect
property are none
, move
, or link
only, and any other combinations are prohibited. The desired operation will change the mouse pointer, so the cursor might appear with a plus for the copy
operation. The desired effect can be modified by a user by pressing the modifier keys. The exact keys vary by platform. On Windows OS, a user typically uses the Shift and Ctrl keys to switch between the copy
, move
, and link
operations. During the dragenter
and dragover
events, we can modify both the effectAllowed
and dropEffect
properties to specify the supported operations by a drop target. The effect specified in dropEffect
must be the one that is listed within the effectAllowed
property. The value of the dropEffect
property can tell us exactly the result of the drag operation. If the value of the dropEffect
property is none
, then the drag was cancelled; otherwise, the specified effect holds the performed operation. The drag-and-drop operation is considered complete after the dragevent
is finished.
The drop target is a place where the dragged item may be dropped. The drop target is very important because most areas of a web page are not permitted to drop. Event listeners for the dragenter
and dragover
events are used to indicate a valid drop target through preventing default handling by canceling events, as shown in the following code:
var dropTarget = querySelector("#sample_drop_id") ..onDragOver.listen((MouseEvent event) { if (checkTarget(event.target)) { event.preventDefault(); event.dataTransfer.dropEffect = 'copy'; } });
In the preceding code, we call the checkTarget
method to be sure that the target is in the right place to be dropped. In our case, the drop target must have the droppable
attribute, as shown in the following code:
bool checkTarget(Element target) { return target.attributes.containsKey('droppable'), }
However, it is common that the drop will be accepted or rejected based on the type of drag data within the dataTransfer
property. In this case, we should check the property types of the dataTransfer
property to decide whether the data can be accepted to be dropped. The code is as follows:
var dropTarget = querySelector("#sample_drop_id") ..onDragOver.listen((MouseEvent event) { if (checkTarget(event.target) && checkTypes(event.dataTransfer.types)) { event.preventDefault(); event.dataTransfer.dropEffect = 'copy'; } });
In the following code, the checkTypes
function accepts only the text/data
types specified through the setData
method of dataTransfer
inside the listener of the dragstart
event:
bool checkTypes(List<String> types) { return types.contains("text/data"); }
You can now run the web application, drag your box with the Drag me!
text, and drop it inside the box with the Drop here
text. Let's polish our application and change the text of the drop zone.
When a user releases the mouse, the drag-and-drop operation ends. If this happened over an element that was identified as a valid drop target, the drag-and-drop API will fire a drop
event at the target. The dataTransfer
property of the drop
event holds the data that is being dragged. To retrieve the dragged data, we will use the getData
method of the dataTransfer
property, as follows:
var dropTarget = querySelector("#sample_drop_id") ..onDragOver.listen((MouseEvent event) { if (checkTarget(event.target) && checkTypes(event.dataTransfer.types)) { event.preventDefault(); event.dataTransfer.dropEffect = 'copy'; } }) ..onDrop.listen((MouseEvent event) { Element dropTarget = event.target; dropTarget.innerHtml = event.dataTransfer.getData('text/plain'), event.preventDefault(); });
The getData
method will retrieve the string value that was set when the setData
method was called. When an empty string is returned from getData
, this means that data of the specified type does not exist. At the end of the getData
method, you need to call the preventDefault
method of the event if you have accepted the drop. Here is the result of the drag-and-drop operation:
Finally, when the drag operation is complete, the drag-and-drop API generates a dragend
event at the source element that received the dragstart
event. The API generates that event regardless of the result of the drag-and-drop operation. The value of the dropEffect
property can tell us the exact result of the drag operation. If the value of the dropEffect
property is none
, then the drag was cancelled; otherwise, the specified effect holds the performed operation. The drag-and-drop operation is considered complete after dragevent
is finished.
3.145.52.188