HTML5 drag API实现列表拖拽排序

拖拽API(Drag and Drop API)是HTML5提供的一组功能,使得在网页上实现拖放操作变得更加简单和强大。这个API允许开发者为网页元素添加拖拽功能,用户可以通过鼠标将元素拖动并放置到指定的目标区域。

事件类型

  • dragstart:事件主体是被拖放元素,在开始拖放被拖放元素时触发。
  • darg:事件主体是被拖放元素,在正在拖放被拖放元素时触发。
  • dragenter:事件主体是目标元素,在被拖放元素进入某元素时触发。
  • dragover:事件主体是目标元素,在被拖放在某元素内移动时触发。
  • dragleave:事件主体是目标元素,在被拖放元素移出目标元素是触发。
  • drop:事件主体是目标元素,在目标元素完全接受被拖放元素时触发。
  • dragend:事件主体是被拖放元素,在整个拖放操作结束时触发。

Draggable属性

HTML元素可以通过设置draggable属性为true来启用拖动。例如:

html 复制代码
<div draggable="true">可拖动的元素</div>

DataTransfer对象

这些事件中的event对象包含一个dataTransfer属性,用于存储和传递拖拽的数据。例如,可以使用dataTransfer.setDatadataTransfer.getData方法来设置和获取拖拽的数据。

列表拖拽排序

演示

列表拖拽排序

完整代码

index.css

css 复制代码
.list {
     padding: 24px 5%;
}

.list .list-item {
    cursor: move;
    user-select: none;
    background-color: royalblue;
    padding: 16px 24px;
    border-radius: 5px;
    margin-bottom: 8px;
    color: white;
}

.list-item.moving {
    background-color: #ccc;
    color: transparent;
}

index.html

html 复制代码
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Document</title>
    <link rel="stylesheet" href="./index.css">
</head>
<body>
    <div class="list">
        <div draggable="true" class="list-item">1</div>
        <div draggable="true" class="list-item">2</div>
        <div draggable="true" class="list-item">3</div>
        <div draggable="true" class="list-item">4</div>
        <div draggable="true" class="list-item">5</div>
    </div>
    <script src="./index.js"></script>
</body>
</html>

index.js

js 复制代码
const list = document.querySelector('.list');
let source;
const handleDragStart = (e) => {
   setTimeout(() => {
       e.target.classList.add('moving'); // 设置拖拽样式
   }, 0);
    source = e.target;
   e.dataTransfer.effectAllowed = 'move'; // 设置鼠标样式
}
const handleDragEnter = (e) => {
    e.preventDefault();
    if (!source) return;
    const target = e.target;
    if (target === list || target === source) return; // 如果进入的元素是父元素或者是被拖动元素则不处理
    const sourceIndex = Array.from(list.children).indexOf(source); // children是类数组,对象类型
    const targetIndex = Array.from(list.children).indexOf(target);
    if (sourceIndex < targetIndex) {
        // 向下拖
        list.insertBefore(target, source); // 将目标元素插入到被拖拽元素之前
    } else {
        // 向上拖
        list.insertBefore(source, target); // 将被拖拽元素插入到目标元素之前
    }
}
const handleDragEnd = (e) => {
    e.target.classList.remove('moving'); // 移除拖拽样式
    source = null;
}

list.addEventListener('dragstart', handleDragStart);
list.addEventListener('dragenter', handleDragEnter);
list.addEventListener('dragend', handleDragEnd);
list.addEventListener('dragover', e => e.preventDefault()); // 目标元素默认不允许被其他元素进入,被拖拽元素会回到原来位置,需要阻止此默认行为

移动端对 Drag API(Drag and Drop API)的支持

支持较差,原因如下:

  1. 原生支持有限

    • 移动端的触摸事件(touchstarttouchmovetouchend)与鼠标事件不同,很多浏览器不会触发 dragstartdragoverdrop 等拖拽相关的事件。
    • iOS 的 Safari 以及 Android 浏览器大多数情况下 不支持 Drag API,特别是在 WebView 内。
  2. 触摸事件冲突

    • 触摸屏幕时,系统会优先触发 touch 事件,而非 drag 事件,导致 Drag API 无法正常工作。
    • 例如,在 iOS 上,touchmove 会默认滚动页面,而不会触发 drag 相关的事件。
  3. 性能和交互体验

    • 在移动端,拖拽操作通常由 手势 代替,比如滑动、长按拖动等。
    • 例如,HTML5 Draggable 属性 在桌面端可以生效,但在移动端一般不会响应 dragstart 事件。

解决方案:

如果想在移动端实现拖拽效果,推荐使用:

  1. Touch Events 代替 Drag API

    • 监听 touchstarttouchmovetouchend 事件,手动计算元素位置并移动。
  2. 第三方库(更方便)

示例代码:

如果你想在移动端实现拖拽,建议使用 touchmove 事件,例如:

移动端拖拽

js 复制代码
const item = document.getElementById("draggable");

let offsetX = 0; // 记录触摸点相对元素左上角的偏移
let offsetY = 0;

item.addEventListener("touchstart", function (event) {
    let touch = event.touches[0];

    // 获取元素当前位置
    let rect = item.getBoundingClientRect();

    // 计算触摸点相对元素左上角的偏移
    offsetX = touch.clientX - rect.left;
    offsetY = touch.clientY - rect.top;

    // 使元素绝对定位
    item.style.position = "absolute";
});

item.addEventListener("touchmove", function (event) {
    event.preventDefault(); // 阻止滚动

    let touch = event.touches[0];

    // 计算元素的新位置,保持触摸点在元素内的相对位置
    let newLeft = touch.clientX - offsetX;
    let newTop = touch.clientY - offsetY;

    item.style.left = newLeft + "px";
    item.style.top = newTop + "px";
});

这样可以在移动端实现类似拖拽的效果,而不依赖 Drag API

相关推荐
Momo__1 小时前
VueUse createReusableTemplate —— 单文件组件内的模板复用神器
前端·vue.js
程序员小富1 小时前
我开源了一个开发者专属的智能 JSON 工具,得到了媳妇高度认可
前端·vue.js·后端
小小小小宇1 小时前
程序员如何给 LLM 装工具以及看懂推理过程
前端
写代码的皮筏艇1 小时前
React中的forwardRef
前端·react.js·面试
槑有老呆1 小时前
花三个月工资请了个 AI 程序员,结果它连青岛啤酒股价都查不了
前端
风骏时光牛马1 小时前
Verilog开发常见问题汇总解析
前端
子兮曰1 小时前
AI Coding Method Map:一张图看懂 AI 编程的完整链路
前端·人工智能·后端
weedsfly1 小时前
语法糖褪去之后——Babel 转译产物中的 JavaScript 本貌
前端·javascript
JustHappy1 小时前
「软件设计思想杂谈🤔」“切图仔”也能懂编译原理?框架源码也许没那么难。聊聊 Vue 的编译(上)
前端·javascript·vue.js