js to achieve drag and drop sorting details

js to achieve drag and drop sorting details

1. Introduction

Drag-and-drop sorting should be familiar to everyone. When working normally, you may choose to use open source libraries such as Sortable.js to meet your needs. But after meeting the requirements, have you ever thought about how to achieve drag and drop sorting? I spent some time researching it and I’m sharing it with you today.

2. Implementation

 {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

.grid {
    display: flex;
    flex-wrap: wrap;
    margin: 0 -15px -15px 0;
    touch-action: none;
    user-select: none;
}

.grid-item {
    width: 90px;
    height: 90px;
    line-height: 88px;
    text-align: center;
    margin: 0 15px 15px 0;
    background: #FFF;
    border: 1px solid #d6d6d6;
    list-style: none;
}

.active {
    background: #c8ebfb;
}

.clone-grid-item {
    position: fixed;
    left: 0;
    top: 0;
    z-index: 1;
    width: 90px;
    height: 90px;
    line-height: 88px;
    text-align: center;
    background: #FFF;
    border: 1px solid #d6d6d6;
    opacity: 0.8;
    list-style: none;
}

<ul class="grid">
    <li class="grid-item">item1</li>
    <li class="grid-item">item2</li>
    <li class="grid-item">item3</li>
    <li class="grid-item">item4</li>
    <li class="grid-item">item5</li>
    <li class="grid-item">item6</li>
    <li class="grid-item">item7</li>
    <li class="grid-item">item8</li>
    <li class="grid-item">item9</li>
    <li class="grid-item">item10</li>
</ul>

Using ES6 Class writing:

class Draggable {
    constructor(options) {
        this.parent = options.element; // Parent element this.cloneElementClassName = options.cloneElementClassName; // Clone element class name this.isPointerdown = false;
        this.diff = { x: 0, y: 0 }; // Difference relative to the last move this.drag = { element: null, index: 0, lastIndex: 0 }; // Drag element this.drop = { element: null, index: 0, lastIndex: 0 }; // Release element this.clone = { element: null, x: 0, y: 0 };
        this.lastPointermove = { x: 0, y: 0 };
        this.rectList = []; // Used to save the data obtained by the drag item getBoundingClientRect() method this.init();
    }
    init() {
        this.getRect();
        this.bindEventListener();
    }
    // Get element position information getRect() {
        this.rectList.length = 0;
        for (const item of this.parent.children) {
            this.rectList.push(item.getBoundingClientRect());
        }
    }
    handlePointerdown(e) {
        // If it is a mouse click, only respond to the left button if (e.pointerType === 'mouse' && e.button !== 0) {
            return;
        }
        if (e.target === this.parent) {
            return;
        }
        this.isPointerdown = true;
        this.parent.setPointerCapture(e.pointerId);
        this.lastPointermove.x = e.clientX;
        this.lastPointermove.y = e.clientY;
        this.drag.element = e.target;
        this.drag.element.classList.add('active');
        this.clone.element = this.drag.element.cloneNode(true);
        this.clone.element.className = this.cloneElementClassName;
        this.clone.element.style.transition = 'none';
        const i = [].indexOf.call(this.parent.children, this.drag.element);
        this.clone.x = this.rectList[i].left;
        this.clone.y = this.rectList[i].top;
        this.drag.index = i;
        this.drag.lastIndex = i;
        this.clone.element.style.transform = 'translate3d(' + this.clone.x + 'px, ' + this.clone.y + 'px, 0)';
        document.body.appendChild(this.clone.element);
    }
    handlePointermove(e) {
        if (this.isPointerdown) {
            this.diff.x = e.clientX - this.lastPointermove.x;
            this.diff.y = e.clientY - this.lastPointermove.y;
            this.lastPointermove.x = e.clientX;
            this.lastPointermove.y = e.clientY;
            this.clone.x += this.diff.x;
            this.clone.y += this.diff.y;
            this.clone.element.style.transform = 'translate3d(' + this.clone.x + 'px, ' + this.clone.y + 'px, 0)';
            for (let i = 0; i < this.rectList.length; i++) {
                // Collision detection if (e.clientX > this.rectList[i].left && e.clientX < this.rectList[i].right &&
                    e.clientY > this.rectList[i].top && e.clientY < this.rectList[i].bottom) {
                    this.drop.element = this.parent.children[i];
                    this.drop.lastIndex = i;
                    if (this.drag.element !== this.drop.element) {
                        if (this.drag.index < i) {
                            this.parent.insertBefore(this.drag.element, this.drop.element.nextElementSibling);
                            this.drop.index = i - 1;
                        } else {
                            this.parent.insertBefore(this.drag.element, this.drop.element);
                            this.drop.index = i + 1;
                        }
                        this.drag.index = i;
                        const dragRect = this.rectList[this.drag.index];
                        const lastDragRect = this.rectList[this.drag.lastIndex];
                        const dropRect = this.rectList[this.drop.index];
                        const lastDropRect = this.rectList[this.drop.lastIndex];
                        this.drag.lastIndex = i;
                        this.drag.element.style.transition = 'none';
                        this.drop.element.style.transition = 'none';
                        this.drag.element.style.transform = 'translate3d(' + (lastDragRect.left - dragRect.left) + 'px, ' + (lastDragRect.top - dragRect.top) + 'px, 0)';
                        this.drop.element.style.transform = 'translate3d(' + (lastDropRect.left - dropRect.left) + 'px, ' + (lastDropRect.top - dropRect.top) + 'px, 0)';
                        this.drag.element.offsetLeft; // trigger redraw this.drag.element.style.transition = 'transform 150ms';
                        this.drop.element.style.transition = 'transform 150ms';
                        this.drag.element.style.transform = 'translate3d(0px, 0px, 0px)';
                        this.drop.element.style.transform = 'translate3d(0px, 0px, 0px)';
                    }
                    break;
                }
            }
        }
    }
    handlePointerup(e) {
        if (this.isPointerdown) {
            this.isPointerdown = false;
            this.drag.element.classList.remove('active');
            this.clone.element.remove();
        }
    }
    handlePointercancel(e) {
        if (this.isPointerdown) {
            this.isPointerdown = false;
            this.drag.element.classList.remove('active');
            this.clone.element.remove();
        }
    }
    bindEventListener() {
        this.handlePointerdown = this.handlePointerdown.bind(this);
        this.handlePointermove = this.handlePointermove.bind(this);
        this.handlePointerup = this.handlePointerup.bind(this);
        this.handlePointercancel = this.handlePointercancel.bind(this);
        this.getRect = this.getRect.bind(this);
        this.parent.addEventListener('pointerdown', this.handlePointerdown);
        this.parent.addEventListener('pointermove', this.handlePointermove);
        this.parent.addEventListener('pointerup', this.handlePointerup);
        this.parent.addEventListener('pointercancel', this.handlePointercancel);
        window.addEventListener('scroll', this.getRect);
        window.addEventListener('resize', this.getRect);
        window.addEventListener('orientationchange', this.getRect);
    }
    unbindEventListener() {
        this.parent.removeEventListener('pointerdown', this.handlePointerdown);
        this.parent.removeEventListener('pointermove', this.handlePointermove);
        this.parent.removeEventListener('pointerup', this.handlePointerup);
        this.parent.removeEventListener('pointercancel', this.handlePointercancel);
        window.removeEventListener('scroll', this.getRect);
        window.removeEventListener('resize', this.getRect);
        window.removeEventListener('orientationchange', this.getRect);
    }
}
// Instantiate new Draggable({
    element: document.querySelector('.grid'),
    cloneElementClassName: 'clone-grid-item'
});

Demo: jsdemo.codeman.top/html/dragga…

3. Why not use HTML drag and drop API?

Because the native HTML drag and drop API cannot be used on mobile devices, PointerEvent event is used to implement the drag logic in order to be compatible with both PC and mobile devices.

4. Summary

The basic function of drag-and-drop sorting has been implemented, but there are still many shortcomings. Features like nested dragging, dragging across lists, and automatic scrolling when dragging to the bottom are not implemented.

This is the end of this article about the details of implementing drag and drop sorting with js. For more relevant content about implementing drag and drop sorting with js, please search for previous articles on 123WORDPRESS.COM or continue to browse the related articles below. I hope you will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • Antdesign-vue combined with sortablejs to achieve the function of dragging and sorting two tables
  • Source code example of image drag and drop sorting based on js
  • React.js component implements drag and drop sorting component function process analysis
  • Elementui table component + sortablejs to implement row drag and drop sorting sample code
  • Implementing drag-and-drop sorting based on AngularJS drag-and-drop plug-in ngDraggable.js
  • JS drag and drop sorting plug-in Sortable.js usage example analysis
  • React.js component implements drag-and-drop copy and sortable sample code
  • JS implements simple image drag and drop sorting example code

<<:  25 Examples of Using Circular Elements in Web Design

>>:  Docker uses Git to implement the detailed process of Jenkins release and test projects

Recommend

Detailed explanation of using JavaScript WeakMap

A WeakMap object is a collection of key/value pai...

How to mount a data disk on Tencent Cloud Server Centos

First, check whether the hard disk device has a d...

Summary of HTML knowledge points for the front end (recommended)

1. HTML Overview htyper text markup language Hype...

Simple analysis of EffectList in React

Table of contents EffectList Collection EffectLis...

Implementation of React virtual list

Table of contents 1. Background 2. What is a virt...

Nginx content cache and common parameter configuration details

Use scenarios: The project's pages need to lo...

Let's talk about the performance of MySQL's COUNT(*)

Preface Basically, programmers in the workplace u...

JavaScript form validation example

HTML forms are commonly used to collect user info...

Detailed Example of Row-Level Locking in MySQL

Preface Locks are synchronization mechanisms used...

A practical record of checking and processing duplicate MySQL records on site

Table of contents Preface analyze Data Total Repe...

How to write memory-efficient applications with Node.js

Table of contents Preface Problem: Large file cop...

How to query a record in Mysql in which page of paging

Preface In practice, we may encounter such a prob...

How to use React forwardRef and what to note

Previously, react.forwardRef could not be applied...

Detailed tutorial on deploying Hadoop cluster using Docker

Recently, I want to build a hadoop test cluster i...