Open In App

Drag and Drop List Using TypeScript

Last Updated : 05 Feb, 2025
Summarize
Comments
Improve
Suggest changes
Like Article
Like
Share
Report
News Follow

A drag-and-drop sortable list in TypeScript lets users reorder items by dragging them to new positions. This is done using dragstart, dragend and dragover events for smooth DOM manipulation.

What We’re Going to Create

We’ll build a drag-and-drop sortable list with features like handling dragstart, dragover, and dragend events to interact with DOM elements.

  • We will make a list in an unordered form.
  • We will then put the heading for the unordered list.
  • The list elements will be put in a vertical flex manner.

Project Preview

Screenshot-2025-01-27-160838
Drag and Drop Sortable List in Typescript

Drag and Drop sortable List - HTML and CSS code

This HTML and CSS code creates a stylish, interactive drag-and-drop sortable list. It uses smooth transitions and visual feedback for reordering items within a container, making the list both functional and visually appealing.

<html>
<head>
    <style>
        body {
            font-family: 'Poppins', sans-serif;
            padding: 20px;
            background: linear-gradient(to right, #6a11cb, #2575fc);
            color: #fff;
            text-align: center;
        }
        h2 {
            margin-bottom: 20px;
            font-size: 1.8em;
        }
        .list {
            list-style: none;
            padding: 0;
            width: 350px;
            margin: auto;
            background: rgba(255, 255, 255, 0.1);
            border-radius: 10px;
            box-shadow: 0 8px 15px rgba(0, 0, 0, 0.2);
        }
        .item {
            padding: 15px 20px;
            margin: 8px 0;
            background: rgba(255, 255, 255, 0.8);
            border-radius: 5px;
            font-size: 1.1em;
            color: #333;
            font-weight: bold;
            cursor: grab;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
            transition: background 0.2s, transform 0.2s;
        }
        .item:hover {
            background: #e8f0ff;
            transform: scale(1.03);
        }
        .dragging {
            opacity: 0.7;
            transform: rotate(-2deg);
        }
        .over {
            border: 2px dashed #ff8c42;
            background: #fff3e0;
        }
    </style>
</head>
<body>
    <h2>Drag and Drop Sortable List</h2>
    <ul class="list">
        <li class="item" draggable="true">Item 1</li>
        <li class="item" draggable="true">Item 2</li>
        <li class="item" draggable="true">Item 3</li>
        <li class="item" draggable="true">Item 4</li>
        <li class="item" draggable="true">Item 5</li>
    </ul>
    <script defer src="lists.js"></script>
</body>
</html>

In this example

  • The code creates a sortable list of items where each item is draggable, allowing the user to reorder the list.
  • The list and its items are styled with a gradient background, soft shadows, and hover effects, making the interface visually appealing and interactive.
  • When an item is dragged, its opacity is reduced, and a dashed border appears on the drop target, providing visual feedback for the user during the drag-and-drop action.

Drag and Drop sortable list – TypeScript Logic

This code implements a drag-and-drop sortable list where items can be rearranged through a smooth drag-and-drop interface. Using JavaScript and TypeScript, it leverages event listeners and DOM manipulation to update the list dynamically.

const list = document.querySelector('.list') as HTMLElement | null;
let dragging: HTMLElement | null = null;

if (list) {
    list.addEventListener('dragstart', (e: DragEvent) => {
        const target = e.target as HTMLElement;
        if (target && target.classList.contains('item')) {
            dragging = target;
            target.classList.add('dragging');
        }
    });

    list.addEventListener('dragend', () => {
        if (dragging) {
            dragging.classList.remove('dragging');
        }
        document.querySelectorAll('.item').
            forEach(item => item.classList.remove('over'));
        dragging = null;
    });

    list.addEventListener('dragover', (e: DragEvent) => {
        e.preventDefault();
        const afterElement = getDragAfterElement(list, e.clientY);

        document.querySelectorAll('.item')
            .forEach(item => item.classList.remove('over'));

        if (afterElement && dragging) {
            afterElement.classList.add('over');
            list.insertBefore(dragging, afterElement);
        } else if (dragging) {
            list.appendChild(dragging);
        }
    });
}

function getDragAfterElement(container: HTMLElement,
    y: number): HTMLElement | null {
    const items = Array.prototype.slice.call(container.querySelectorAll<HTMLElement>
        ('.item:not(.dragging)')) as HTMLElement[];

    return items.reduce((closest, child) => {
        const box = child.getBoundingClientRect();
        const offset = y - box.top - box.height / 2;
        if (offset < 0 && offset > closest.offset) {
            return { offset, element: child };
        }
        return closest;
    }, { offset: Number.NEGATIVE_INFINITY, element: null }).element;
}

In this example

  • When a list item is dragged, the dragging class is added to it, and the item is marked as the active dragged element.
  • Once dragging ends, the dragging class is removed, and all items reset their hover state (over class).
  • During dragging, the code identifies the item being dragged over and temporarily adds the over class, allowing the dragged item to be inserted before or after another item.
  • The getDragAfterElement function calculates the closest item to the mouse pointer and positions the dragged item accordingly.

Convert to JavaScript File

Now You need to convert the TypeScript file into JavaScript to render by browser. Use one of the following command.

npx  tsc lists.ts
tsc lists.ts
  • The command tsc lists.ts compiles the lists.js TypeScript file into a lists.js JavaScript file.
  • It places the output in the same directory as the input file by default.

Complete Code

<html>
<head>
    <style>
        body {
            font-family: 'Poppins', sans-serif;
            padding: 20px;
            background: linear-gradient(to right, #6a11cb, #2575fc);
            color: #fff;
            text-align: center;
        }
        h2 {
            margin-bottom: 20px;
            font-size: 1.8em;
        }
        .list {
            list-style: none;
            padding: 0;
            width: 350px;
            margin: auto;
            background: rgba(255, 255, 255, 0.1);
            border-radius: 10px;
            box-shadow: 0 8px 15px rgba(0, 0, 0, 0.2);
        }
        .item {
            padding: 15px 20px;
            margin: 8px 0;
            background: rgba(255, 255, 255, 0.8);
            border-radius: 5px;
            font-size: 1.1em;
            color: #333;
            font-weight: bold;
            cursor: grab;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
            transition: background 0.2s, transform 0.2s;
        }
        .item:hover {
            background: #e8f0ff;
            transform: scale(1.03);
        }
        .dragging {
            opacity: 0.7;
            transform: rotate(-2deg);
        }
        .over {
            border: 2px dashed #ff8c42;
            background: #fff3e0;
        }
    </style>
</head>
<body>
    <h2>Drag and Drop Sortable List</h2>
    <ul class="list">
        <li class="item" draggable="true">Item 1</li>
        <li class="item" draggable="true">Item 2</li>
        <li class="item" draggable="true">Item 3</li>
        <li class="item" draggable="true">Item 4</li>
        <li class="item" draggable="true">Item 5</li>
    </ul>
    <script defer>
        const list = document.querySelector('.list');
        let dragging = null;
        if (list) {
            list.addEventListener('dragstart', function (e) {
                const target = e.target;
                if (target && target.classList.contains('item')) {
                    dragging = target;
                    target.classList.add('dragging');
                }
            });
            list.addEventListener('dragend', function () {
                if (dragging) {
                    dragging.classList.remove('dragging');
                }
                document.querySelectorAll('.item').forEach(function (item) {
                    item.classList.remove('over');
                });
                dragging = null;
            });
            list.addEventListener('dragover', function (e) {
                e.preventDefault();
                const afterElement = getDragAfterElement(list, e.clientY);
                document.querySelectorAll('.item').forEach(function (item) {
                    item.classList.remove('over');
                });
                if (afterElement && dragging) {
                    afterElement.classList.add('over');
                    list.insertBefore(dragging, afterElement);
                } else if (dragging) {
                    list.appendChild(dragging);
                }
            });
        }
        function getDragAfterElement(container, y) {
            const items = Array.from(container.querySelectorAll('.item:not(.dragging)'));
            return items.reduce(function (closest, child) {
                const box = child.getBoundingClientRect();
                const offset = y - box.top - box.height / 2;
                if (offset < 0 && offset > closest.offset) {
                    return { offset, element: child };
                }
                return closest;
            }, { offset: Number.NEGATIVE_INFINITY, element: null }).element;
        }
    </script>
</body>
</html>

Next Article

Similar Reads

three90RightbarBannerImg