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

相关推荐
拉不动的猪8 分钟前
前端小白之 CSS弹性布局基础使用规范案例讲解
前端·javascript·css
伍哥的传说13 分钟前
React强大且灵活hooks库——ahooks入门实践之开发调试类hook(dev)详解
前端·javascript·react.js·ecmascript·hooks·react-hooks·ahooks
界面开发小八哥43 分钟前
界面控件Kendo UI for Angular 2025 Q2新版亮点 - 增强跨设备的无缝体验
前端·ui·界面控件·kendo ui·angular.js
枷锁—sha2 小时前
从零掌握XML与DTD实体:原理、XXE漏洞攻防
xml·前端·网络·chrome·web安全·网络安全
F2E_Zhangmo2 小时前
基于cornerstone3D的dicom影像浏览器 第二章,初始化页面结构
前端·javascript·vue·cornerstone3d·cornerstonejs
代码的余温2 小时前
如何区别HTML和HTML5?
前端·html·html5
天下无贼!2 小时前
【样式效果】纯CSS从零到一实现动态彩色背景效果
前端·css
DoraBigHead2 小时前
手写 `new`、`call`、`apply`、`bind` + V8 函数调用机制解密
前端·javascript·面试
_pengliang2 小时前
css 音频波浪动画-小程序\PC可用
前端·css·小程序
ai小鬼头2 小时前
AIStarter教你快速打包GPT-SoVITS-v2,解锁AI应用市场新玩法
前端·后端·github