likes
comments
collection
share

如何实现列表拖拽排序?

作者站长头像
站长
· 阅读数 32

1.前言

拖拽排序是一种常见的交互方式,它可以允许用户通过鼠标或手势来移动元素位置,从而实现对某个序列的重新排序。在Web应用程序中,拖拽排序被广泛应用于列表、图库、文件夹等场景中,使得用户可以更加方便地对其中的元素进行管理和操作。

拖拽排序的实现方式有多种,其中比较常见的是利用HTML5的Drag and Drop API。该API提供了丰富的事件和方法,能够轻松地实现元素的拖拽、拖入和拖出等功能。同时,结合JavaScript和CSS,也可以实现一些比较高级的特效和交互,如元素的拖拽限制、拖拽时的动画效果等。

在本篇文章中,我们将介绍如何使用HTML5 Drag and Drop API实现一个简单的拖拽排序组件,并详细说明其实现原理和优化方法。同时,我们还会探讨其他实现拖拽排序的方式,并给出一些实用的代码示例和最佳实践。希望读者通过本文的学习,能够理解拖拽排序的基本原理和实现方法,为自己的项目落地实现提供一些有价值的参考。

2.HTML5拖拽Drag相关API介绍

  1. ondragstart:当元素被开始拖动时触发,通过该事件可以设置传递给放置目标的数据。

  2. ondrag:在元素正在拖拽时触发。

  3. ondragenter:在一个可放置的目标上面拖拽时触发,可以用于改变放置目标的样式。

  4. ondragover:在一个可放置的目标上面移动时触发,可以用于阻止默认行为并显示拖拽效果。

  5. ondragleave:从一个可放置的目标上面拖拽离开时触发,可以用于恢复放置目标的样式。

  6. ondrop:在一个可放置的目标上释放时触发,通过该事件可以获取传递给放置目标的数据。

  7. ondragend:在元素已经结束拖拽时触发,可以用于清理拖拽操作相关的数据。

3.如何实现元素拖拽?

在现今的应用交互中, 元素拖拽是一项很常见的功能。要想实现元素拖拽,首先需要设置元素的可拖拽属性draggable=true,然后再给元素添加拖拽事件即可实现。

3.1 创建列表元素并设置可拖拽属性

function createList(){
    const _ul = document.createElement("ul");

    _ul.className = 'wrap-list';

    list.forEach(item=>{
        const _li = document.createElement("li");

        _li.className = 'item';
        
        // 设置可拖拽
        _li.draggable = true;

        _li.innerHTML =  `<p>${item}</p>`;

        _ul.appendChild(_li);
    })

    return _ul;
}

3.2 渲染DOM

function renderDOM(){
    const _ulDOM = createList();

    container.appendChild(_ulDOM);
}

3.3 事件处理

function bindEvent(){
    const _wrapList = document.querySelector(".wrap-list");
    
    const _items = document.querySelectorAll(".item");

    // 给列表容器添加事件
    _wrapList.addEventListener('dragover', onDragoverHander, false);

    // 阻止默认行为
    _wrapList.addEventListener('dragenter', (e)=>e.preventDefault(), false);

    // 阻止默认行为
    window.addEventListener('dragover', (e)=>e.preventDefault(), false);

    // 阻止默认行为
    window.addEventListener('dragenter', (e)=>e.preventDefault(), false);

    // 遍历Item添加拖拽事件
    _items.forEach((item)=> {
        item.addEventListener('dragstart', onDragStartHandler, false);
        item.addEventListener('dragend', onDragEndHandler, false);
    })
}

3.4 处理拖拽逻辑(元素交换)

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

    const _wrapList = this;

    // 获取正在被拖拽的节点
    const _dragging = _wrapList.querySelector(".dragging");

    // 获取除了拖拽的节点之外的节点
    const _sibItems = _wrapList.querySelectorAll('.item:not(.dragging)');

    // 找到要拖拽相邻的元素
    const _sibItem = [..._sibItems].find((item)=>e.clientY <= item.offsetTop + item.offsetHeight / 2);

    // 交换位置
    _wrapList.insertBefore(_dragging, _sibItem);
}

4.应用场景

  • 拖拽排序:最常见的应用场景之一是实现列表的排序。用户可以通过拖拽某个元素来改变其在列表中的位置。例如,网页上的音乐播放列表或者任务管理器都可以通过拖拽元素来实现排序。

  • 文件上传:拖拽文件到指定区域进行上传,这样可以避免繁琐的文件选择过程,提高用户体验。大多数现代浏览器都支持将文件拖拽到指定区域进行上传。

  • 图片库操作:在网站中展示图片库时,用户可以通过拖拽图片到其他区域实现图片的复制、移动和删除等操作。例如在线相册或者图库。

  • 窗口操作:用户可以通过拖拽窗口的标题栏来改变窗口的位置,也可以通过拖拽边界来改变窗口的大小。在桌面应用程序和Web应用程序中都可以看到这种形式的拖拽操作。

  • 游戏设计:在HTML5游戏开发中,拖拽可以作为操作方式之一,例如在拼图游戏中,用户可以通过拖拽碎片来完成拼图。

这些场景只是HTML5元素拖拽的一部分应用场景,HTML5元素拖拽可以应用于各种类型的Web应用程序中。因为它提高了用户交互性和易用性,并且通过让用户直接操作来提高了生产率,所以广泛使用。

5.列表拖拽排序源码

// 列表数据
const list = ['苹果','香蕉','黄梨','西瓜','葡萄','山竹','榴莲'];

// 获取列表容器元素
const container = document.querySelector(".container");

/**
 * @desc 入口函数
*/
function init(){
    renderDOM();

    bindEvent();
}

/**
 * @desc 事件绑定
*/
function bindEvent(){
    const _wrapList = document.querySelector(".wrap-list");
    
    const _items = document.querySelectorAll(".item");

    _wrapList.addEventListener('dragover', onDragoverHander, false);

    _wrapList.addEventListener('dragenter', (e)=>e.preventDefault(), false);

    window.addEventListener('dragover', (e)=>e.preventDefault(), false);

    window.addEventListener('dragenter', (e)=>e.preventDefault(), false);

    _items.forEach((item)=> {
        item.addEventListener('dragstart', onDragStartHandler, false);
        item.addEventListener('dragend', onDragEndHandler, false);
    })
}

/**
 * @desc 开始拖拽
*/
function onDragStartHandler(){
    const item = this;

    setTimeout(()=> item.classList.add('dragging'), 0);
}

/**
 * @desc 拖转结束
*/
function onDragEndHandler(){
    const item = this;

    item.classList.remove('dragging');
}


/**
 * @desc 渲染dom函数
*/
function renderDOM(){
    const _ulDOM = createList();

    container.appendChild(_ulDOM);
}

/**
 * @desc 创建列表
*/
function createList(){
    const _ul = document.createElement("ul");

    _ul.className = 'wrap-list';

    list.forEach(item=>{
        const _li = document.createElement("li");

        _li.className = 'item';

        _li.draggable = true;

        _li.innerHTML =  `<p>${item}</p>`;

        _ul.appendChild(_li);
    })

    return _ul;
}

/**
 * 拖拉到当前节点上方时,在当前节点上持续触发
*/
function onDragoverHander(e){
    e.preventDefault();

    const _wrapList = this;

    // 获取正在被拖拽的节点
    const _dragging = _wrapList.querySelector(".dragging");

    // 获取除了拖拽的节点之外的节点
    const _sibItems = _wrapList.querySelectorAll('.item:not(.dragging)');

    // 找到要拖拽相邻的元素
    const _sibItem = [..._sibItems].find((item)=>e.clientY <= item.offsetTop + item.offsetHeight / 2);

    // 交换位置
    _wrapList.insertBefore(_dragging, _sibItem);
}

init(); // 启动入口
转载自:https://juejin.cn/post/7237366474473521211
评论
请登录