<!DOCTYPE html>
<html>
<head>
<title>拖拽排序</title>
<style>
#container {
display: flex;
}
.item {
width: 100px;
height: 50px;
background-color: lightblue;
margin: 10px;
text-align: center;
line-height: 50px;
cursor: move;
user-select: none; /* 防止拖拽时选中文字 */
}
.dragging {
opacity: 0.5;
}
.over {
background-color: lightgreen;
}
</style>
</head>
<body>
<div id="container">
<div class="item" draggable="true">Item 1</div>
<div class="item" draggable="true">Item 2</div>
<div class="item" draggable="true">Item 3</div>
</div>
<script>
const container = document.getElementById('container');
const items = document.querySelectorAll('.item');
let draggedItem = null;
items.forEach(item => {
item.addEventListener('dragstart', (e) => {
draggedItem = item;
item.classList.add('dragging');
e.dataTransfer.effectAllowed = 'move'; // 设置允许的操作为移动
e.dataTransfer.setData('text/plain', null); // 这行对于 Firefox 很重要
});
item.addEventListener('dragend', () => {
draggedItem.classList.remove('dragging');
items.forEach(item => item.classList.remove('over')); // 清除所有 over 样式
draggedItem = null;
});
item.addEventListener('dragover', (e) => {
e.preventDefault(); // 必须preventDefault(),否则drop事件不会触发
e.dataTransfer.dropEffect = 'move'; // 视觉反馈
const target = e.target.closest('.item'); // 确保目标是.item元素
if (target && target !== draggedItem) {
const rect = target.getBoundingClientRect();
const offsetY = e.clientY - rect.top;
const targetCenter = rect.height / 2;
if (offsetY < targetCenter) {
container.insertBefore(draggedItem, target);
} else {
container.insertBefore(draggedItem, target.nextSibling);
}
items.forEach(item => item.classList.remove('over')); // 清除之前的 over 样式
target.classList.add('over');
}
});
item.addEventListener('dragenter', (e) => {
const target = e.target.closest('.item');
if (target && target !== draggedItem) {
target.classList.add('over');
}
});
item.addEventListener('dragleave', (e) => {
const target = e.target.closest('.item');
if (target) {
target.classList.remove('over');
}
});
item.addEventListener('drop', (e) => {
e.preventDefault(); // 阻止默认行为
});
});
</script>
</body>
</html>
关键步骤和代码解释:
-
设置
draggable
属性: 在需要拖拽的元素上设置draggable="true"
。 -
dragstart
事件: 当开始拖拽元素时触发。在这个事件中,记录被拖拽的元素,并设置拖拽数据和效果。e.dataTransfer.setData('text/plain', null);
对于 Firefox 正常工作至关重要,即使你不需要传输任何数据。 -
dragend
事件: 拖拽结束时触发。清除拖拽相关的样式和变量。 -
dragover
事件: 当拖拽的元素在目标元素上方时触发。必须调用e.preventDefault()
才能触发drop
事件。此事件用于处理元素插入位置的逻辑。 通过计算鼠标相对于目标元素中心的位置来确定插入位置。 -
dragenter
和dragleave
事件: 用于添加和移除目标元素的 hover 样式,提供视觉反馈。使用 `closest
vuedraggable拖拽排序插件:
<template>
<div style="border: 1px solid #e8e8e8;width: 300px;">
<draggable v-model="reportList" @end="endEvent" v-bind="dragOptions">
<p v-for="r in reportList" :key="r.id" :class="r.active ? 'active' : 'disabled'">{
{r.name}}</p>
</draggable>
</div>
</template>
<script>
import draggable from "vuedraggable"
export default {
components: {
draggable
},
data () {
return {
dragOptions: {
animation: 500,
filter: '.disabled'
},
reportList: [
{ id: 1, name: 'javascript', active: true},
{ id: 2, active: false, name:'css' },
{ id: 3, active: false, name: 'typescript' },
{ id: 4, active: false, name: 'vue.js' },
{ id: 5, active: false, name: 'nodejs' }
]
}
},
methods: {
endEvent () {
console.log(this.reportList) // 自动更新reportList
}
}
}
</script>