• Skip to primary navigation
  • Skip to main content
  • Skip to primary sidebar
JavaScriptSource

JavaScriptSource

Search 5,000+ Free JavaScript Snippets

  • Home
  • Browse Snippets
    • Addon
    • Ajax
    • Buttons
    • Cookies
    • CSS
    • Featured
    • Forms
    • Games
    • Generators
    • Image Effects
    • Math Related
    • Miscellaneous
    • Multimedia
    • Navigation
    • Page Details
    • Passwords
    • Text Effects
    • Time & Date
    • User Details
Home / Miscellaneous / Drag and drop element in a list

Drag and drop element in a list

Drag and drop element in a list

Create a sortable list whose items can be dragged and dropped inside it.

<div id="list">
    <div class="draggable">A</div>
    <div class="draggable">B</div>
    <div class="draggable">C</div>
    <div class="draggable">D</div>
    <div class="draggable">E</div>
</div>

Each item has class of draggable indicating that user can drag it:

.draggable {
    cursor: move;
    user-select: none;
}

Make items draggable

By using the following approach, we can turn each item into a draggable element:

// The current dragging item
let draggingEle;

// The current position of mouse relative to the dragging element
let x = 0;
let y = 0;

const mouseDownHandler = function (e) {
    draggingEle = e.target;

    // Calculate the mouse position
    const rect = draggingEle.getBoundingClientRect();
    x = e.pageX - rect.left;
    y = e.pageY - rect.top;

    // Attach the listeners to `document`
    document.addEventListener('mousemove', mouseMoveHandler);
    document.addEventListener('mouseup', mouseUpHandler);
};

const mouseMoveHandler = function (e) {
    // Set position for dragging element
    draggingEle.style.position = 'absolute';
    draggingEle.style.top = `${e.pageY - y}px`;
    draggingEle.style.left = `${e.pageX - x}px`;
};

The mouseup event handler will remove the position styles of dragging item and cleans up the event handlers:

const mouseUpHandler = function () {
    // Remove the position styles
    draggingEle.style.removeProperty('top');
    draggingEle.style.removeProperty('left');
    draggingEle.style.removeProperty('position');

    x = null;
    y = null;
    draggingEle = null;

    // Remove the handlers of `mousemove` and `mouseup`
    document.removeEventListener('mousemove', mouseMoveHandler);
    document.removeEventListener('mouseup', mouseUpHandler);
};

Now we can attach the mousedown event to each item by looping over the list of items:

// Query the list element
const list = document.getElementById('list');

// Query all items
[].slice.call(list.querySelectorAll('.draggable')).forEach(function (item) {
    item.addEventListener('mousedown', mouseDownHandler);
});

Add a placeholder

Let’s take a look at the list of items again:

A B C D E

When we drag an item, C for example, the next item (D) will move up to the top and takes the area of the dragging element (C). To fix that, we create a dynamic placeholder element and insert it right before the dragging element. The height of placeholder must be the same as dragging element.

The placeholder is created once during the mouse moving, so we add a new flag isDraggingStarted to track it:

let placeholder;
let isDraggingStarted = false;

const mouseMoveHandler = function(e) {
    const draggingRect = draggingEle.getBoundingClientRect();

    if (!isDraggingStarted) {
        // Update the flag
        isDraggingStarted = true;

        // Let the placeholder take the height of dragging element
        // So the next element won't move up
        placeholder = document.createElement('div');
        placeholder.classList.add('placeholder');
        draggingEle.parentNode.insertBefore(
            placeholder,
            draggingEle.nextSibling
        );

        // Set the placeholder's height
        placeholder.style.height = `${draggingRect.height}px`;
    }

    ...
}

The placeholder will be removed as soon as the users drop the item:

const mouseUpHandler = function() {
    // Remove the placeholder
    placeholder && placeholder.parentNode.removeChild(placeholder);
    // Reset the flag
    isDraggingStarted = false;

    ...
};

Here is the order of element when user drags and moves an item around:

A B placeholder <- The dynamic placeholder C <- The dragging item D E

Determine if user moves item up or down

First of all, we need a helper function to check if an item is above or below another one.

A nodeA is treated as above of nodeB if the horizontal center point of nodeA is less than nodeB. The center point of a node can be calculated by taking the sum of its top and half of its height:

const isAbove = function (nodeA, nodeB) {
    // Get the bounding rectangle of nodes
    const rectA = nodeA.getBoundingClientRect();
    const rectB = nodeB.getBoundingClientRect();

    return rectA.top + rectA.height / 2 < rectB.top + rectB.height / 2;
};

As user moves the item around, we define the previous and next sibling items:

const mouseMoveHandler = function (e) {
    // The current order:
    // prevEle
    // draggingEle
    // placeholder
    // nextEle
    const prevEle = draggingEle.previousElementSibling;
    const nextEle = placeholder.nextElementSibling;
};

If user moves the item to the top, we will swap the placeholder and the previous item:

const mouseMoveHandler = function(e) {
    ...

    // User moves item to the top
    if (prevEle && isAbove(draggingEle, prevEle)) {
        // The current order    -> The new order
        // prevEle              -> placeholder
        // draggingEle          -> draggingEle
        // placeholder          -> prevEle
        swap(placeholder, draggingEle);
        swap(placeholder, prevEle);
        return;
    }
};

Similarly, we will swap the next and dragging item if we detect that user moves item down to the bottom:

const mouseMoveHandler = function(e) {
    ...

    // User moves the dragging element to the bottom
    if (nextEle && isAbove(nextEle, draggingEle)) {
        // The current order    -> The new order
        // draggingEle          -> nextEle
        // placeholder          -> placeholder
        // nextEle              -> draggingEle
        swap(nextEle, placeholder);
        swap(nextEle, draggingEle);
    }
};

Here, swap is a small function for swapping two nodes:

const swap = function (nodeA, nodeB) {
    const parentA = nodeA.parentNode;
    const siblingA = nodeA.nextSibling === nodeB ? nodeA : nodeA.nextSibling;

    // Move `nodeA` to before the `nodeB`
    nodeB.parentNode.insertBefore(nodeA, nodeB);

    // Move `nodeB` to before the sibling of `nodeA`
    parentA.insertBefore(nodeB, siblingA);
};

Source

https://htmldom.dev/drag-and-drop-element-in-a-list/

Miscellaneous

Related Snippets:

  • Xmas Tree Generator
  • Replace a portion of a string with something else
  • Get and set the text of an element (without markup)
  • Copy text to the clipboard

Primary Sidebar

Popular Posts

Story Generator

IP Grabber – get a users IP address with JavaScript

Simple Calendar

Remove Ads

Astrological Calculator

Copyright © 2025 JavaScriptSource.com

  • About
  • Privacy Policy
  • FAQ
  • Jobs For Developers