Advance JavaScript (DOM, BOM, Web API) / Web API

Drag & Drop

The Drag and Drop API lets users grab an element with the mouse, move it around, and place it in a designated drop zone. It’s commonly used in file upload areas, customizable dashboards, editors, and interactive UI components.

A complete drag-and-drop interaction involves two main HTML elements:

  1. Draggable element – the item the user picks up and moves.

  2. Drop target – the area where the draggable item can be released.


🚧 Events Fired on Draggable Elements

When an element is dragged, the following events occur in sequence:

  • dragstart – Triggered as soon as the user clicks and begins dragging the element. The cursor changes to indicate that dropping on itself isn't allowed.

  • drag – Fires continuously while the user is actively dragging the element.

  • dragend – Fires when the user releases the mouse button and the dragging stops.

In all these events, e.target refers to the element being dragged.

The browser does not automatically alter the appearance of the dragged item, so you can style or animate it as needed.


📎 Events Fired on Drop Targets

When the dragged element interacts with a drop zone, events occur in this order:

  • dragenter – Fires when the dragged item first enters the drop target’s boundaries.

  • dragover – Fires repeatedly as long as the dragged item remains over the drop target.

  • dragleave or drop –

    • dragleave fires when the dragged item leaves the drop target’s boundaries.

    • drop fires when the user releases the dragged item inside the drop target.

For all these events, e.target refers to the drop target element.


✅ Valid Drop Target

  • A valid drop target is any HTML element that is allowed to accept a dragged item.

  • Even though most elements can listen to drop-related events (dragenter, dragover, dragleave, drop), they DO NOT accept drops by default.

  • If dropping isn’t allowed, the drop event will never fire.

  • To make an element a valid drop target, you must:

    1. Listen to the dragover event

    2. Call event.preventDefault() inside it (this enables dropping)

    3. Optionally handle the drop event to process the dropped data


💥 Transferring Data with dataTransfer (Simplified)

  • During a drag-and-drop operation, data is shared using the dataTransfer object, available on drag events.

  • It lets you send data from the dragged element → to the drop target.

  • You use two main methods:

1️⃣ setData() – Store data while dragging

dataTransfer.setData(format, data)
  • format → e.g., "text/plain" or "text/uri-list"

  • data → a string you want to attach to the drag action

Example:

event.dataTransfer.setData("text/plain", "Hello");

2️⃣ getData() – Retrieve the data on drop

dataTransfer.getData(format)
  • format must match what you used in setData()

  • Returns the string stored earlier, or an empty string if nothing was set

Example:

const text = event.dataTransfer.getData("text/plain");

📍Drag and Drop Example

We’ll develop the following simple drag-and-drop application to demonstrate the JavaScript drag and drop API :

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta-name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JavaScript - Drag and Drop Demo</title>
    <link rel="stylesheet" href="css/style.css">
</head>

<body>
    <div class="container">
        <h1>JavaScript - Drag and Drop</h1>
        <div class="drop-targets">
            <div class="box">
                <div class="item" id="item" draggable="true"></div>
            </div>
            <div class="box"></div>
            <div class="box"></div>
        </div>
    </div>

    <script src="js/app.js"></script>
</body>
</html>
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
    font-size: 16px;
    background-color: #fff;
    overflow: hidden;
}

h1 {
    color: #323330;
}

.container {
    height: 100vh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    margin: 20px;

}

.drop-targets {
    display: flex;
    flex-direction: row;
    justify-content: space-around;
    align-items: center;
    margin: 20px 0;
}

.box {
    height: 150px;
    width: 150px;
    border: solid 3px #ccc;
    margin: 10px;

    /* align items in the box */
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;

}

.drag-over {
    border: dashed 3px red;
}

.item {
    height: 75px;
    width: 75px;
    background-color: #F0DB4F;

}

.hide {
    display: none;
}
// Select draggable element
const item = document.querySelector('.item');

// Enable dragstart
item.addEventListener('dragstart', dragStart);

function dragStart(e) {
    e.dataTransfer.setData('text/plain', e.target.id);

    // hide the element visually but keep space
    setTimeout(() => {
        e.target.classList.add('hide');
    }, 0);
}

// Select all drop zones
const boxes = document.querySelectorAll('.box');

boxes.forEach(box => {
    box.addEventListener('dragenter', dragEnter);
    box.addEventListener('dragover', dragOver);
    box.addEventListener('dragleave', dragLeave);
    box.addEventListener('drop', drop);
});

function dragEnter(e) {
    e.preventDefault();

    // highlight ONLY the box, not child elements
    if (e.target.classList.contains('box')) {
        e.target.classList.add('drag-over');
    }
}

function dragOver(e) {
    e.preventDefault();
    if (e.target.classList.contains('box')) {
        e.target.classList.add('drag-over');
    }
}

function dragLeave(e) {
    if (e.target.classList.contains('box')) {
        e.target.classList.remove('drag-over');
    }
}

function drop(e) {
    if (e.target.classList.contains('box')) {
        e.target.classList.remove('drag-over');

        // Retrieve dragged item
        const id = e.dataTransfer.getData('text/plain');
        const draggable = document.getElementById(id);

        // Append draggable to new box
        e.target.appendChild(draggable);

        // Reveal it again
        draggable.classList.remove('hide');
    }
}