HTML5 drag API实现列表拖拽排序

devtools/2025/3/21 4:47:12/

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

事件类型

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

Draggable属性

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

<div draggable="true">可拖动的元素</div>

DataTransfer对象

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

列表拖拽排序

演示

列表拖拽排序

完整代码

index.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

<!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

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. 第三方库(更方便)

    • SortableJS(支持 PC 和移动端)
    • Interact.js(提供更丰富的拖拽交互)

示例代码:

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

移动端拖拽

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


http://www.ppmy.cn/devtools/168801.html

相关文章

为什么渲染农场渲染帧而非视频?核心原因 + 举例

核心原因 举例 1. 并行加速 原理&#xff1a;每帧独立计算&#xff0c;多台电脑同时工作。 举例&#xff1a;100 帧动画→100 台电脑各渲 1 帧&#xff0c;1 小时完成&#xff08;单台需 100 小时&#xff09; 2. 改错成本低 原理&#xff1a;单帧出错仅需重渲该帧&#xf…

STM32之I2C硬件外设

注意&#xff1a;硬件I2C的引脚是固定的 SDA和SCL都是复用到外部引脚。 SDA发送时数据寄存器的数据在数据移位寄存器空闲的状态下进入数据移位寄存器&#xff0c;此时会置状态寄存器的TXE为1&#xff0c;表示发送寄存器为空&#xff0c;然后往数据控制寄存器中一位一位的移送数…

大模型学习-从零开始在colab训练大模型

目录 写这篇文章的目的 1.准备训练所需的文件 2.将压缩包上传到谷歌云盘 使用colab 3.训练 写这篇文章的目的 这篇文章是对&#xff1a;大模型学习-在colab中训练并更换模型_colab调整模型-CSDN博客的一个优化&#xff0c;因为在之前的博文中&#xff0c;我是提供了一个现…

Java高频面试之集合-15

hello啊&#xff0c;各位观众姥爷们&#xff01;&#xff01;&#xff01;本baby今天来报道了&#xff01;哈哈哈哈哈嗝&#x1f436; 面试官&#xff1a;解决哈希冲突有哪些方法&#xff1f; 1. 开放寻址法&#xff08;Open Addressing&#xff09; 核心思想&#xff1a;当哈…

【Go】函数闭包、堆和栈的概念

闭包 闭包机制解析 在函数式编程中&#xff0c;闭包&#xff08;Closure&#xff09; 是一种特殊的函数结构&#xff0c;其核心特性是能够捕获并持有外部函数的上下文环境变量。这一机制打破了传统函数中局部变量的生命周期规则&#xff1a; 常规局部变量 在函数被调用时创建…

蓝桥杯备考:特殊01背包问题——》集合subset

我们划分成两个集合&#xff0c;实际上我们只需要看一部分就行了&#xff0c;也就是从集合的所有元素里挑出恰好满足集合总和的一半儿&#xff0c;当然&#xff0c;如果我们的集合总和是奇数的话&#xff0c;我们是无论如何也挑不出刚好一半儿的&#xff0c;因为我们没有小数&a…

【C++———IO流】

听专情的古人&#xff0c;把美言留给最爱的人........................................................................................ 文章目录 前言 一、【C/C IO流】 1、【C语言的输入与输出】 2、【CIO流引入】 二、【C标准IO流——】 2.1、【cin&&cout】 2.2…

Vue渲染函数 - render 函数

文章目录 Vue渲染函数 - render 函数1. 什么是 render 函数2、页面展示过程3、render 函数的参数4. 如何使用&#xff08;1&#xff09;基本渲染&#xff08;2&#xff09;传递属性和事件&#xff08;3&#xff09;条件渲染 5. render 函数的实际使用6.View Design 组件中的使用…