How to Drag & Drop HTML Elements and Files using Javascript
Are you trying to add the drag & drop functionality to your website, but you don’t know exactly how to do it? Well, worry not, this story explains all you need to know to get you started.
To be able to drag & drop HTML elements or just drop files in your website, the following steps will be your best friend. Well, until you add the feature to your website. Then, it will be bye bye friendship.
Just note that the steps are based on the HTML drag & drop API explained in MDN web docs.
1) Detect the Drag & Drop Feature Support in Your Browser
Before using the HTML drag & drop API, add feature detection code to ensure browser support.
The following code is the feature detection code for the HTML drag & drop API from Modernizr.
2) Decide which HTML Element is going to be dragged
In order to make a chosen HTML element be a draggable element, the element must have the following:
- The draggable attribute, which determines whether the element can be dragged or not.
- The ondragstart event handler, which executes when the drag event starts.
The following is an example of identifying a paragraph element <p> as a draggable element.
3) Determine the Data of the Drag Event in the dragstart event handler
So what exactly is the drag event’s data?
The drag event’s data is the data to include in the drag operation. In other words, it’s the data that will be available when the element is dropped after being dragged.
In order to define the drag event’s data, you add the data using the method setData() on the dataTransfer property of the event. This property maintains the drag event’s data and state.
The drag event’s data consists of drag items. Each drag item consist of two parts, which are the data value and the type of the data. You can add as many drag items as you like.
The type is typically a MIME type and can also be a custom type and each type corresponds to a string value that represents the data’s value. However, note that each type can only be used once per event, so the same type cannot be assigned to different values in the same drag event.
The following is an example of defining the event’s data in dragstart event handler.
3.1. Guide on How to Determine the Most Suitable Type for the Drag Event’s Data based on the Dragged Element
- Dragging Text
For dragging text, you should include only a single drag items, where it’s defined as the following:
Just note that, as stated by MDN web docs, dragging text in text boxes and selections happens automatically, so you don’t really need to handle it as the browser does it for you.
The following is an example of the recommended type for dragging text.
event.dataTransfer.setData("text/plain", "text")
Also, note that you should always add the data of type text/plain even for non-textual elements as this serves as a fallback case for apps or drop zones that don’t support your other listed types. Just make sure you add it last as it’s the least specific.
- Dragging Links
For dragging links, you should include two drag items, where it’s defined as the following:
The second drag item is the fallback type, as discussed previously, that must be less specific than the URI type for when an application or a drop target does not support the desired type.
The following is an example of the recommended drag type for a dragged link.
event.dataTransfer.setData("text/uri-list","https://medium.com/");event.dataTransfer.setData("text/plain","https://medium.com/");
What if you want to include multiple links?
In order to include multiple links in the drag event’s data, in the text/uri-list data, separate each link with a line break.
A line that begins with a number sign (#) is a comment and should not be considered a valid URL. For the text/plain type of the drag data, it should include all links but exclude the comments.
The following is an example of how multiple links should be separated when multiple links need to be dragged.
# First Link
https://www.medium.com/
# Second Link
http://www.example.com
When retrieving the dropped link(s) in the drop event (explained later), you should handle the case where multiple links might have been included in the drag data. The following example demonstrates that.
However, if you want to access the first URL only, you can use “URL” instead of “uri-list” with the getData() method (explained later). The following example demonstrates that. Just note that the type “URL” shouldn’t be used when setting the data though.
var url = event.dataTransfer.getData("URL"); //returns the first link in the list
- Dragging Images
For dragging images, where images are usually dragged by their URL, you should include two drag items defined as the following:
The second drag item is the fallback type, as discussed previously, that must be less specific than the URI type for when an application or a drop target does not support the desired type.
Please read the dragging links section above for more details about the text/uri-list drag type, where you can also see how it is possible to pass multiple links in a single dragging event.
[Note] There are other ways of dragging images, for example using the mozSetDataAt() method but that function is only supported in Firefox at the time of this research.
The following is an example of the recommended drag type for dragging an image.
event.dataTransfer.setData("text/uri-list", imageUrl);event.dataTransfer.setData("text/plain", imageUrl);
- Dragging HTML and XML Content
All types of dragged elements are HTML elements such as text and links, excluding files. However, what if we want an HTML’s inner text to be the drag data? This is what dragging HTML content means.
For dragging HTML’s inner text, you should include two drag items such as the following:
For dragging an XML content, you should include two drag items:
The second drag item is the fallback type, as discussed previously, that must be less specific than the text/html or the text/xml type for when an application or a drop target does not support the desired type.
The following is an example of the recommended drag type for an HTML content. For XML, it should be very similar to this example.
event.dataTransfer.setData("text/html", "Hello <strong>World</strong>!");event.dataTransfer.setData("text/plain","Hello World!");
- Dragging DOM Nodes/Elements
Hmm, so what’s the difference between the HTML content and a DOM node or element, which could possibly refer to an HTML node?
HTML content refers to the inner HTML of an HTML element, whereas the DOM node refers to the node reference, for example the HTML node reference.
So, one way to drag DOM nodes or elements is by using the application/x-moz-node type, as stated by MDN docs, where the data for that type is the actual DOM node.
Another alternative is to use the DOM element’s id name or class name as the drag data using the text/plain type and access that info at the drop event handler to get the element. Note the drop event will be explained later.
The following is an example for dragging DOM nodes using the element’s id.
event.dataTransfer.setData("text/plain", event.target.id);
- Dragging Custom Data
Custom types can also be used when dragging custom data, where the data associated with it would be a stringified form of the custom data.
You should include the text/plain fallback type as the second drag item, as discussed previously, unless you want the object to not be dropped elsewhere then it shouldn’t include the plaintext alternative.
Therefore, including the custom type only prevents the custom data from being dropped elsewhere.
The following is an example of the recommended drag type for dragging custom data.
- Dragging Files
Please refer to the next section of this post as it explains how a file from the OS can be dragged and dropped in a website.
4) Determine a Custom Drag Image, if the Default Image is not Desired
When dragging a draggable element, a translucent image is automatically created from that element. This image is called the drag image and it follows the cursor.
However, if you’d rather use a custom image, use the method setDragImage() on the dataTrasnfer property, which must be handled in the dragstart event handler.
The following is an example of defining the drag image, when a custom image is desired.
5) Determine the Drop Effect
The drop effect is the type of operation the drag and drop will result to, which can be copy, paste or just linking.
Based on MDN docs’s definition, the possible drop effects (operations) are defined as the following:
1. copy indicates that the dragged data will be copied from its present location to the drop location.
2. move indicates that the dragged data will be moved from its present location to the drop location.
3. link indicates that some form of relationship or connection will be created between the source and drop locations.
Knowing the drop effect (type of operation) helps control the sort of feedback that should be presented to the user, which can be the copying or moving of HTML elements. That feedback must be implemented in the drop stage with the help of the drop effect property to know which drop effect was desired.
To define a drop effect, the following steps must be followed:
5.1. Determine the Allowed Drop Effect(s) on the Draggable Element in it’s dragstart Event Handler
Using the effectAllowed property on the dataTransfer event property, you define all drop effects that are allowed to be applied for that draggable element.
This is done as different drop locations can be defined and different drop effects can be handled per drop location.
The following table defines the possible effectAllowed property values based on MDN docs:
5.2. For each Drop Location, Define the Drop Effect(s) for it in it’s dragover Event Handler
The to be defined drop effect must be one of the allowed effects determined at the dragstart event handler of the source location.
When a drop effect is not one of the allowed drop effects, it will be set to one of the allowed drop effects.
The dragover handler, where the drop effect should be defined, is executed when the dragged element is being dragged over a possible drop location.
The following is an example of defining the drop effects for a particular drop location.
6) Define a Drop Zone (Drop Location)
By default, no element in the HTML document allows a drop. However, that can be changed and in order to make an element a drop zone, it must have:
- The ondragover event handler, which is executed when the draggable element is being dragged over this element. In this event handler, the drop effect of the drop location is defined as previously mentioned in step 5.2.
- The ondrop event handler, which is the executed when the draggable element is dropped in the drag location/zone.
The following is an example of defining an element as a drop location.
[Note] In both the dragover and the drop event handlers, preventDefault() must be called to allow a drop as it indicates that a drop is allowed at that location.
[Be Aware] If a drop will only be allowed in certain situations where conditions are used, then only add preventDefault if conditions for allowing a drop are met. This is done to prevent having the element be a drop zone if conditions are not met where drop shouldn’t be allowed.
[Also, Note] Rejecting a drop based on the type of the drag data in the dataTransfer property is a common case. The following example demonstrates that by allowing only links to be dropped in the drop zone element that has the dragover handler.
7) Handle the Element’s Drop
During the drop event, the drag data should be processed. To do that, the getData() method on the dataTransfer event property is used.
The getData() method retrieves the drag items as it takes a single argument of the type of data to retrieve and it will return the string value associated with it.
The allowedEffect and dropEffect properties help control the feedback that will be presented to the user.
Handling such feedback is the responsibility of the developer and should be implemented in the drop event handler. For example, retrieving an element and appending it to another parent will move it, but copying the node will result in a copy effect.
[Note] If multiple drop effects are defined for a single drop zone, the dropEffect property should be used to determine which drag operation was desired for this particular drop event.
The following is an example of handling a drop.
[Note] Sometimes, you may have supported different types of the same data but only want to retrieve the most specific type. The following code from MDN docs demonstrates that.
8) Use dragend Event to Check if Drop was Successful
At the end of a drag operation, a dragend event fires at the source element. The event fires whether the drag was successful or got canceled.
To check if the drag operation was successful or not, the value of the dropEffect can be checked as the following:
- If the dropEffect property’s value is “none” in the dragend event handler, then it means the drag operations got cancelled.
- Otherwise, the value of the dropEffect property is the operation that got performed successfully.
Hmm, so how is this useful?
The source element can use this information after a successful move operation to remove the dragged item from the old location.
Demo on HTML Element’s Drag & Drop
Note: you can also clone the project from my Github here.
1) Detect the Drag & Drop Feature Support in Your Browser
Before using the HTML drag & drop API, add feature detection code to ensure browser support.
The following code is the feature detection code for the HTML drag & drop API from Modernizr.
2) Define a Drop Zone (Drop Location)
By default, no element in the HTML document allows a drop. However, that can be changed and in order to make an element a drop zone, it must have:
- The ondragover event handler, which is executed when the file(s) is/are being dragged over the drop location.
- The ondrop event handler, which is the executed when the file(s) is/are dropped in the drop location.
[Note] As stated by MDN docs:
dragstart and dragend events are not fired when dragging a file into the browser from the OS.
The following is an example for defining an element as a drop location.
3) Handle the File’s Drop
When the user drops the file(s) in a drop zone, a drop event will be triggered. During the drop event, the file(s) should be processed as desired.
To access each dropped file, use the event’s DataTransfer property to access the files property. After accessing a dropped file, use the File API to process it.
[Note] If the browser supports the DataTransferItemList property, you can use it with the getAsFile() method instead of using the DataTransfer property to access the file.
[Also, Note] In both the dragover and the drop event handlers, preventDefault() must be called to allow a drop as it indicates that a drop is allowed at that location. It also prevents the file from being opened, which is the default behavior that occurs when dropping a file in a web page.
[Be Aware] If a drop will only be allowed in certain situations where conditions are used, then only add preventDefault if conditions for allowing a drop are met. This is done to prevent having the element be a drop zone if conditions are not met where drop shouldn’t be allowed.
The following example, from MDN docs, demonstrates processing dropped files, where a drag item that is not a file is ignored. Both possible ways are demonstrated in this
Demo on HTML Element’s Drag & Drop
Note: you can also clone the project from my Github here.