Making Content Draggable
Browsers only make certain content draggable by default—typically text selections, images, and links. To enable dragging on other elements, set the draggable attribute to true. Any element can be made draggable: images, files, links, or arbitrary markup.
In a typical drag-and-drop interface, you work with three roles: a source element where the drag starts, the data payload being dragged, and a target that accepts the drop. Not all elements make valid targets—an image, for instance, cannot serve as one.
Tracking the Drag Lifecycle
Seven events fire during a drag sequence. Listen for them on the source, targets, or both, depending on the behavior you need:
dragstartdragdragenterdragleavedragoverdropdragend
For a basic rearrangement interface, you attach a dragstart handler to each draggable source element. This handler fires when the drag sequence begins and is the natural place give visual feedback that the element is being moved. For example, you can dim the source by lowering its opacity, then restore it in the dragend handler—which fires when the drag sequence finishes, regardless of whether the operation succeeded.
Communicating Drop Targets
Elements that can accept a drop need visual cues so users know a valid target exists. The dragenter, dragover, and dragleave events let you react as the pointer enters, crosses, and exits potential targets.
Use dragenter to add a target-highlighting class, and remove it on dragleave. Avoid toggling classes in dragover: that event fires continuously while the user holds an item over a target, causing repeated class toggles and excessive rendering work. If you do need dragover, throttle or debounce the listener.
One critical detail: dragover's default action sets dataTransfer.dropEffect to "none", which prevents the drop event from ever firing. Call e.preventDefault() in your dragover handler to override this and make the target receptive.
Handling the Drop
The drop event handler performs the actual data transfer. The browser's default behavior for drops is generally an unwanted redirect, so stop it by calling e.preventDefault() and e.stopPropagation() at the start of your handler.
The real payload travels in the dataTransfer object's files property:
// Access file data dropped from the desktop
const files = e.dataTransfer.files;
This same object is what you use to move your own UI elements. For reordering markup like columns, you store the source's HTML in dragstart and read it in the drop handler to swap the positions.
Data Payload and Visual Feedback Controls
Three dataTransfer properties give you control over the drag's behavior and how it presents to the user:
dataTransfer.effectAllowed restricts the drag operation for the source element, accepting values like none, copy, copyLink, copyMove, link, linkMove, move, all, and uninitialized.
dataTransfer.dropEffect controls the cursor feedback on the target during dragenter and dragover. Valid values are none, copy, link, and move.
e.dataTransfer.setDragImage(imgElement, x, y) replaces the browser's default ghost image with a custom icon.
File Uploads from Your Desktop
A common drag-and-drop pattern is letting users drop files from their OS onto a web page. The logic mirrors element dragging, with the drop handler differing: your data isn't in the MIME-typed payload but in the dataTransfer.files property. From there, you process each File object as you would with any file input.
Implementation Notes and Limitations
The entire process—source, target, and data—works together within one unified API. Regardless of the operation's outcome, make sure to clean up classes you added during dragenter in your dragend handler to keep the interface consistent.
Keep in mind the HTML Drag and Drop API is a desktop browser feature; it's not supported on mobile devices, so your interface needs a fallback for touch users.



