Drag and Drop Events

Implement drag and drop functionality

JavaScriptAdvanced
JavaScript
// Method 1: HTML5 Drag and Drop API
const draggable = document.getElementById('draggable');
const dropzone = document.getElementById('dropzone');

// Make element draggable
draggable.draggable = true;

// Drag events on source
draggable.addEventListener('dragstart', function(e) {
    console.log('Drag started');
    e.dataTransfer.setData('text/plain', draggable.id);
    e.dataTransfer.effectAllowed = 'move';
    draggable.style.opacity = '0.5';
});

draggable.addEventListener('dragend', function(e) {
    console.log('Drag ended');
    draggable.style.opacity = '1';
});

// Drop events on target
dropzone.addEventListener('dragover', function(e) {
    e.preventDefault(); // Allow drop
    e.dataTransfer.dropEffect = 'move';
    dropzone.style.backgroundColor = 'lightblue';
});

dropzone.addEventListener('dragleave', function(e) {
    dropzone.style.backgroundColor = '';
});

dropzone.addEventListener('drop', function(e) {
    e.preventDefault();
    console.log('Dropped');
    const data = e.dataTransfer.getData('text/plain');
    const element = document.getElementById(data);
    dropzone.appendChild(element);
    dropzone.style.backgroundColor = '';
});

// Method 2: Custom drag implementation
let isDragging = false;
let currentX, currentY, initialX, initialY;

const customDraggable = document.getElementById('customDraggable');

customDraggable.addEventListener('mousedown', function(e) {
    isDragging = true;
    initialX = e.clientX - customDraggable.offsetLeft;
    initialY = e.clientY - customDraggable.offsetTop;
    customDraggable.style.cursor = 'grabbing';
});

document.addEventListener('mousemove', function(e) {
    if (isDragging) {
        e.preventDefault();
        currentX = e.clientX - initialX;
        currentY = e.clientY - initialY;
        
        customDraggable.style.left = currentX + 'px';
        customDraggable.style.top = currentY + 'px';
    }
});

document.addEventListener('mouseup', function() {
    if (isDragging) {
        isDragging = false;
        customDraggable.style.cursor = 'grab';
    }
});

// Method 3: Drag with touch support
customDraggable.addEventListener('touchstart', function(e) {
    isDragging = true;
    const touch = e.touches[0];
    initialX = touch.clientX - customDraggable.offsetLeft;
    initialY = touch.clientY - customDraggable.offsetTop;
});

document.addEventListener('touchmove', function(e) {
    if (isDragging) {
        e.preventDefault();
        const touch = e.touches[0];
        currentX = touch.clientX - initialX;
        currentY = touch.clientY - initialY;
        
        customDraggable.style.left = currentX + 'px';
        customDraggable.style.top = currentY + 'px';
    }
});

document.addEventListener('touchend', function() {
    isDragging = false;
});

// Method 4: Drag with constraints
function dragWithConstraints(element, minX, minY, maxX, maxY) {
    let isDragging = false;
    let startX, startY, offsetX, offsetY;
    
    element.addEventListener('mousedown', function(e) {
        isDragging = true;
        startX = e.clientX;
        startY = e.clientY;
        offsetX = element.offsetLeft;
        offsetY = element.offsetTop;
    });
    
    document.addEventListener('mousemove', function(e) {
        if (isDragging) {
            let newX = offsetX + (e.clientX - startX);
            let newY = offsetY + (e.clientY - startY);
            
            // Apply constraints
            newX = Math.max(minX, Math.min(maxX, newX));
            newY = Math.max(minY, Math.min(maxY, newY));
            
            element.style.left = newX + 'px';
            element.style.top = newY + 'px';
        }
    });
    
    document.addEventListener('mouseup', function() {
        isDragging = false;
    });
}

// Method 5: Sortable list
const sortableList = document.getElementById('sortableList');

sortableList.addEventListener('dragstart', function(e) {
    if (e.target.tagName === 'LI') {
        e.target.style.opacity = '0.5';
        e.dataTransfer.effectAllowed = 'move';
        e.dataTransfer.setData('text/html', e.target.innerHTML);
    }
});

sortableList.addEventListener('dragover', function(e) {
    e.preventDefault();
    const afterElement = getDragAfterElement(sortableList, e.clientY);
    const dragging = document.querySelector('.dragging');
    
    if (afterElement == null) {
        sortableList.appendChild(dragging);
    } else {
        sortableList.insertBefore(dragging, afterElement);
    }
});

function getDragAfterElement(container, y) {
    const draggableElements = [...container.querySelectorAll('li:not(.dragging)')];
    
    return draggableElements.reduce((closest, child) => {
        const box = child.getBoundingClientRect();
        const offset = y - box.top - box.height / 2;
        
        if (offset < 0 && offset > closest.offset) {
            return { offset: offset, element: child };
        } else {
            return closest;
        }
    }, { offset: Number.NEGATIVE_INFINITY }).element;
}

Output

// Output depends on drag and drop interaction

Drag and drop enables interactive UI.

HTML5 Drag and Drop API

  • draggable: Make element draggable
  • dragstart: Drag begins
  • dragend: Drag ends
  • dragover: Over drop target
  • dragleave: Leave drop target
  • drop: Item dropped

Data Transfer

  • setData(): Set drag data
  • getData(): Get drop data
  • effectAllowed: Allowed effects
  • dropEffect: Visual feedback

Custom Implementation

  • Mouse events
  • Touch events
  • Position tracking
  • Constraints

Use Cases

  • File uploads
  • Sortable lists
  • Drag to reorder
  • Interactive games

Best Practices

  • Provide visual feedback
  • Handle touch events
  • Add constraints
  • Test accessibility