Skip to content

Commit 3788c5b

Browse files
committed
New guide
1 parent 7450964 commit 3788c5b

File tree

5 files changed

+355
-54
lines changed

5 files changed

+355
-54
lines changed

files/en-us/web/api/html_drag_and_drop_api/drag_data_store/index.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,19 @@ The {{domxref("DragEvent")}} interface has a {{domxref("DragEvent.dataTransfer",
1010

1111
## DataTransfer, DataTransferItem, and DataTransferItemList
1212

13-
Fundamentally, the drag data store is a list of items, represented as a {{domxref("DataTransferItemList")}} of {{domxref("DataTransferItem")}} objects. Generally, it _does not_ represent multiple resources being transferred, but the same resource encoded in different ways, so that the receiving end can choose the most appropriate supported interpretation. The items are intended to be sorted in descending order of preference. Each item can be one of two [kinds](/en-US/docs/Web/API/DataTransferItem/kind):
13+
Fundamentally, the drag data store is a list of items, represented as a {{domxref("DataTransferItemList")}} of {{domxref("DataTransferItem")}} objects. Each item can be one of two [kinds](/en-US/docs/Web/API/DataTransferItem/kind):
1414

1515
- `string`: its payload is a string, retrievable with {{domxref("DataTransferItem.getAsString", "getAsString()")}}.
1616
- `file`: its payload is a file object, retrievable with {{domxref("DataTransferItem.getAsFile", "getAsFile()")}} (or {{domxref("DataTransferItem.getAsFileSystemHandle", "getAsFileSystemHandle()")}} or {{domxref("DataTransferItem.webkitGetAsEntry", "webkitGetAsEntry()")}}, if more complex file system operations are needed).
1717

18-
Furthermore the item is also identified by a [type](/en-US/docs/Web/API/DataTransferItem/type), which by convention is in the form of a [MIME type](/en-US/docs/Web/HTTP/Guides/MIME_types). This type can instruct the consumer about how the payload should be parsed or decoded. The list can only have one item of each type, so the list, in effect, is a {{jsxref("Map")}} from type strings to payloads.
18+
Furthermore the item is also identified by a [type](/en-US/docs/Web/API/DataTransferItem/type), which by convention is in the form of a [MIME type](/en-US/docs/Web/HTTP/Guides/MIME_types). This type can instruct the consumer about how the payload should be parsed or decoded. For all text items, the list can only have one item of each type, so the list, in effect, contains two disjoint collections: a list of files with potentially duplicate types, and a {{jsxref("Map")}} of text items keyed by their type. Generally, the files list represents multiple files being dragged. The text map _does not_ represent multiple resources being transferred, but the same resource encoded in different ways, so that the receiving end can choose the most appropriate supported interpretation. The text items are intended to be sorted in descending order of preference.
1919

2020
This list is accessible via the {{domxref("DataTransfer.items")}} property.
2121

2222
The HTML Drag and Drop API went through multiple iterations, resulting in two coexisting ways to manage the data store. Before the `DataTransferItemList` and `DataTransferItem` interfaces, the "old way" used the following properties on `DataTransfer`:
2323

2424
- {{domxref("DataTransfer.types", "types")}}: contains the `type` properties of the _text items_ in the list, plus the value `"files"` if there are any _file items_.
25-
- {{domxref("DataTransfer.setData", "setData()")}}, {{domxref("DataTransfer.getData", "getData()")}}, {{domxref("DataTransfer.clearData", "clearData()")}}: operate on the _text items_ in the list using the "type-to-payload mapping" model.
25+
- {{domxref("DataTransfer.setData", "setData()")}}, {{domxref("DataTransfer.getData", "getData()")}}, {{domxref("DataTransfer.clearData", "clearData()")}}: provide access to the _text items_ in the list using the "type-to-payload mapping" model.
2626
- {{domxref("DataTransfer.files", "files")}}: provides access to the _file items_ in the list
2727

2828
You may see that the types of the _file items_ are not directly exposed. They are still accessible, but only via the {{domxref("Blob.type", "type")}} property of each {{domxref("File")}} object in the `files` list, so if you can't read the files, then you can't know their types either (see [reading the drag data store](#reading_the_drag_data_store) for when the store is readable). It is now recommended to just use the `items` property because it provides a more flexible and consistent interface.
@@ -47,7 +47,7 @@ const p1 = document.getElementById("p1");
4747
p1.addEventListener("dragstart", dragstartHandler);
4848
```
4949

50-
For both methods, if they are called when the data store is unmodifiable, nothing happens. If an item with the same type already exists, `add()` throws an error while `setData()` overwrites the existing item.
50+
For both methods, if they are called when the data store is unmodifiable, nothing happens. If a text item with the same type already exists, `add()` throws an error while `setData()` overwrites the existing item.
5151

5252
To add file data to the drag data store, the "new way" still uses the {{domxref("DataTransferItemList.add()")}} method. Because the "old way" stores file items in the {{domxref("DataTransfer.files")}} property, which is a read-only {{domxref("FileList")}}, there's no direct equivalent.
5353

@@ -280,4 +280,3 @@ This allows an arbitrary file to be downloaded when dragged to the file explorer
280280

281281
- [HTML Drag and Drop API (Overview)](/en-US/docs/Web/API/HTML_Drag_and_Drop_API)
282282
- [Drag Operations](/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Drag_operations)
283-
- [HTML Living Standard: Drag and Drop](https://html.spec.whatwg.org/multipage/interaction.html#dnd)

files/en-us/web/api/html_drag_and_drop_api/drag_operations/index.md

Lines changed: 42 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ page-type: guide
88

99
Central to the Drag and Drop API are the various [drag events](/en-US/docs/Web/API/HTML_Drag_and_Drop_API#drag_events) that fire in a specific order and are expected to be handled in a specific way. This document describes the steps that occur during a drag and drop operation, and what the application is supposed to do within each handler.
1010

11+
At a high level, here are the possible steps in a drag and drop operation:
12+
13+
- The user [initiates the drag](#starting_a_drag) on a source node; the {{domxref("HTMLElement/dragstart_event", "dragstart")}} event is fired on the source node. Within this event, the source node prepares the context for the drag operation, including the drag data, feedback image, and allowed drop effects.
14+
- The user [drags the item around](#dragging_over_elements_and_specifying_drop_targets): every time a new element is entered, the {{domxref("HTMLElement/dragenter_event", "dragenter")}} event is fired on that element, and the {{domxref("HTMLElement/dragleave_event", "dragleave")}} event is fired on the previous element. Every few hundred milliseconds, a {{domxref("HTMLElement/dragover_event", "dragover")}} event is fired on the element the drag is currently inside, and the {{domxref("HTMLElement/drag_event", "drag")}} event is fired on the source node.
15+
- The drag enters a valid drop target: the drop target cancels its `dragover` event to indicate that it is a valid drop target. Some form of [drop feedback](#drop_feedback) indicates the expected drop effect to the user.
16+
- The user [performs the drop](#performing_a_drop): the {{domxref("HTMLElement/drop_event", "drop")}} event is fired on the drop target. Within this event, the target node reads the drag data.
17+
- The [drag operation ends](#finishing_the_drag): the {{domxref("HTMLElement/dragend_event", "dragend")}} event is fired on the source node. This event is fired regardless of whether the drop was successful or not.
18+
1119
## Starting a drag
1220

1321
The drag starts on a [draggable item](/en-US/docs/Web/API/HTML_Drag_and_Drop_API#draggable_items), which can be a selection, a draggable element (including links, images, and any element with `draggable="true"`), a file from the operating system's file explorer, etc. First, the {{domxref("HTMLElement/dragstart_event", "dragstart")}} event is fired on the _source node_, which is the draggable element or, for selections, the text node that the drag started on. If this event is cancelled, then the drag operation is aborted. Otherwise, the {{domxref("HTMLElement/pointercancel_event", "pointercancel")}} event is also fired on the source node.
@@ -42,15 +50,17 @@ draggableElement.addEventListener("dragstart", (event) => {
4250
When a drag occurs, a translucent image is generated from the source node, and follows the user's pointer during the drag. This image is created automatically, so you do not need to create it yourself. However, you can use {{domxref("DataTransfer.setDragImage","setDragImage()")}} to specify a custom drag feedback image.
4351

4452
```js
45-
event.dataTransfer.setDragImage(image, xOffset, yOffset);
53+
draggableElement.addEventListener("dragstart", (event) => {
54+
event.dataTransfer.setDragImage(image, xOffset, yOffset);
55+
});
4656
```
4757

4858
Three arguments are necessary. The first is a reference to an image. This reference will typically be to an `<img>` element, but it can also be to a `<canvas>` or any other element. The feedback image will be generated from whatever the image looks like on screen, although for images, they will be drawn at their original size. The second and third arguments to the {{domxref("DataTransfer.setDragImage","setDragImage()")}} method are offsets where the image should appear relative to the mouse pointer.
4959

5060
It is also possible to use images and canvases that are not in a document. This technique is useful when drawing custom drag images using the canvas element, as in the following example:
5161

5262
```js
53-
function dragWithCustomImage(event) {
63+
draggableElement.addEventListener("dragstart", (event) => {
5464
const canvas = document.createElement("canvas");
5565
canvas.width = canvas.height = 50;
5666

@@ -62,20 +72,40 @@ function dragWithCustomImage(event) {
6272
ctx.lineTo(50, 0);
6373
ctx.stroke();
6474

65-
const dt = event.dataTransfer;
66-
dt.setData("text/plain", "Data to Drag");
67-
dt.setDragImage(canvas, 25, 25);
68-
}
75+
event.dataTransfer.setDragImage(canvas, 25, 25);
76+
});
6977
```
7078

7179
In this example, we make one canvas the drag image. As the canvas is 50×50 pixels, we use offsets of half of this (`25`) so that the image appears centered on the mouse pointer.
7280

7381
## Dragging over elements and specifying drop targets
7482

75-
For the entire course of the drag operation, all device input events (such as mouse or keyboard) are suppressed. Every few hundred milliseconds, a {{domxref("HTMLElement/drag_event", "drag")}} event is fired at the source node.
83+
For the entire course of the drag operation, all device input events (such as mouse or keyboard) are suppressed. The dragged data can be moved over various elements in the document, or even elements in other documents. Whenever a new element is entered, a {{domxref("HTMLElement/dragenter_event", "dragenter")}} event is fired on that element, and a {{domxref("HTMLElement/dragleave_event", "dragleave")}} event is fired on the previous element.
84+
85+
> [!NOTE]
86+
> `dragleave` always fires _after_ `dragenter`, so conceptually, in between these two events, the target has entered a new element but has not exited the previous one yet.
87+
88+
Every few hundred milliseconds, two events fire: a {{domxref("HTMLElement/drag_event", "drag")}} event at the source node, and a {{domxref("HTMLElement/dragover_event", "dragover")}} event at the element the drag is currently inside. Most areas of a web page or application are not valid places to drop data, so elements by default ignore any drop that happened on it. The element can elect itself to be a valid drop target by cancelling the `dragover` event. If the element is an editable text field, such as a {{HTMLElement("textarea")}} or [`<input type="text">`](/en-US/docs/Web/HTML/Reference/Elements/input/text), and the data store contains one `text/plain` item, then the element is a valid drop target by default without cancelling `dragover`.
89+
90+
```html
91+
<div id="drop-target">You can drag and then drop a draggable item here</div>
92+
```
93+
94+
```js
95+
const dropElement = document.getElementById("drop-target");
96+
97+
dropElement.addEventListener("dragover", (event) => {
98+
event.preventDefault();
99+
});
100+
```
76101

77102
> [!NOTE]
78-
> The spec requires that if you cancel this `drag` event, the drag operation is considered [aborted](#a_failed_drop); in practice no browser implements this. See example below:
103+
> The spec requires the `dragenter` event to be cancelled too for a drop target, otherwise the `dragover` or `dragleave` events won't even start firing on this element; in practice no browser implements this, and the "current element" changes every time a new element is entered.
104+
105+
> [!NOTE]
106+
> The spec requires that cancelling the `drag` event [aborts](#a_failed_drop) the drag; in practice no browser implements this. See example below:
107+
>
108+
> {{EmbedLiveSample("cancel_drag", "", 100)}}
79109
80110
```html hidden live-sample___cancel_drag
81111
<p draggable="true" id="draggable">Drag me for 1 second!</p>
@@ -100,31 +130,7 @@ draggableElement.addEventListener("drag", (event) => {
100130
});
101131
```
102132

103-
{{EmbedLiveSample("cancel_drag", "", 100)}}
104-
105-
In the course of the drag operation, the dragged data can be moved over various elements in the document, or even elements in other documents. Whenever a new element is entered, a {{domxref("HTMLElement/dragenter_event", "dragenter")}} event is fired on that element, and a {{domxref("HTMLElement/dragleave_event", "dragleave")}} event is fired on the previous element.
106-
107-
> [!NOTE]
108-
> `dragleave` always fires _after_ `dragenter`, so conceptually, in between these two events, the target has entered a new element but has not exited the previous one yet.
109-
110-
Every few hundred milliseconds, a {{domxref("HTMLElement/dragover_event", "dragover")}} event is fired on the element the drag is currently inside. Most areas of a web page or application are not valid places to drop data, so elements by default ignore any drop that happened on it. The element can elect itself to be a valid drop target by cancelling the `dragover` event. If the element is an editable text field, such as a {{HTMLElement("textarea")}} or [`<input type="text">`](/en-US/docs/Web/HTML/Reference/Elements/input/text), and the data store contains one `text/plain` item, then the element is a valid drop target by default without cancelling `dragover`.
111-
112-
```html
113-
<div id="drop-target">You can drag and then drop a draggable item here</div>
114-
```
115-
116-
```js
117-
const dropElement = document.getElementById("drop-target");
118-
119-
dropElement.addEventListener("dragover", (event) => {
120-
event.preventDefault();
121-
});
122-
```
123-
124-
> [!NOTE]
125-
> The spec requires the `dragenter` event to be cancelled too, otherwise the `dragover` or `dragleave` events won't even start firing on this element; in practice no browser implements this, and the "current element" changes every time a new element is entered.
126-
127-
However, you will commonly wish to call the {{domxref("Event.preventDefault","preventDefault()")}} method only in certain situations (for example, only if a link is being dragged). To do this, check a condition and only cancels the event when the condition is met. For example, you can check if the dragged data contains links:
133+
However, you usually only want the drop target to accept drops in certain situations (for example, only if a link is being dragged). To do this, check a condition and only cancel the event when the condition is met. For example, you can check if the dragged data contains links:
128134

129135
```js
130136
dropElement.addEventListener("dragover", (event) => {
@@ -173,7 +179,7 @@ Note that setting `dropEffect` only indicates the desired effect _at this partic
173179
For both user gestures and programmatically setting `dropEffect`, by default, all three drop effects are available. The draggable element can restrict itself to only allow certain effects by setting the {{domxref("DataTransfer.effectAllowed","effectAllowed")}} property within a {{domxref("HTMLElement/dragstart_event", "dragstart")}} event listener.
174180

175181
```js
176-
element.addEventListener("dragstart", (event) => {
182+
draggableElement.addEventListener("dragstart", (event) => {
177183
event.dataTransfer.effectAllowed = "copyLink";
178184
});
179185
```
@@ -339,6 +345,8 @@ target.addEventListener("drop", (event) => {
339345

340346
for more information about how to read, see [Working with the drag data store](/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Drag_data_store#reading_the_drag_data_store).
341347

348+
It is also the source and the target elements' responsibility to collaborate to implement the `dropEffect`—the source listens for the `dragend` event and the target listens for the `drop` event. For example, if the `dropEffect` is `move`, then one of these elements must remove the dragged item from its old location (usually the source element itself, because the target element doesn't necessarily know or have control over the source).
349+
342350
## A failed drop
343351

344352
The drag-and-drop operation is considered failed if one of the following is true:
@@ -366,4 +374,3 @@ After the {{domxref("HTMLElement/dragend_event", "dragend")}} event has finished
366374

367375
- [HTML Drag and Drop API (Overview)](/en-US/docs/Web/API/HTML_Drag_and_Drop_API)
368376
- [Working with the drag data store](/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Drag_data_store)
369-
- [HTML Living Standard: Drag and Drop](https://html.spec.whatwg.org/multipage/interaction.html#dnd)

0 commit comments

Comments
 (0)