HTML5 drag API实现列表拖拽排序
拖拽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.setData
和dataTransfer.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)的支持
支持较差,原因如下:
-
原生支持有限
- 移动端的触摸事件(
touchstart
、touchmove
、touchend
)与鼠标事件不同,很多浏览器不会触发dragstart
、dragover
、drop
等拖拽相关的事件。 - iOS 的 Safari 以及 Android 浏览器大多数情况下 不支持
Drag API
,特别是在 WebView 内。
- 移动端的触摸事件(
-
触摸事件冲突
- 触摸屏幕时,系统会优先触发
touch
事件,而非drag
事件,导致Drag API
无法正常工作。 - 例如,在 iOS 上,
touchmove
会默认滚动页面,而不会触发drag
相关的事件。
- 触摸屏幕时,系统会优先触发
-
性能和交互体验
- 在移动端,拖拽操作通常由 手势 代替,比如滑动、长按拖动等。
- 例如,HTML5
Draggable
属性 在桌面端可以生效,但在移动端一般不会响应dragstart
事件。
解决方案:
如果想在移动端实现拖拽效果,推荐使用:
-
Touch Events 代替
Drag API
- 监听
touchstart
、touchmove
、touchend
事件,手动计算元素位置并移动。
- 监听
-
第三方库(更方便)
- 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
。