Vue3中列表拖拽排序的實(shí)現(xiàn)示例
本文主要內(nèi)容分三個(gè)部分。先寫了在 Vue3 中利用 HTML5 的 draggable 屬性手寫實(shí)現(xiàn)列表拖拽排序的功能。接下來記錄了在 Element Plus 組件庫中結(jié)合 sortable.js 使用,對(duì)表格組件 el-table進(jìn)行拖拽排序的。最后一個(gè)部分是 vuedraggable 拖拽組件的使用。
本文基于
Vite、Vue3、Element Plus技術(shù)棧
draggable 實(shí)現(xiàn)拖拽排序
屬性和事件
draggable 屬性是 HTML5 新增的可拖拽屬性。
在 HTML 中,除了圖像、鏈接和選擇的文本默認(rèn)可拖拽外,其他元素默認(rèn)是不可拖拽的。如果想讓其他元素變成可拖拽的,首先需要把 draggable 屬性設(shè)置為 true。
<p draggable="true"> 可拖拽</p>
設(shè)置完成之后,還需要結(jié)合一些事件才能完成拖拽:
拖拽元素的事件事件
- 拖拽元素的事件
| 事件 | 觸發(fā)時(shí)機(jī) |
|---|---|
dragstart | 開始拖拽時(shí)執(zhí)行 1 次 |
drag | 拖拽開始后多次觸發(fā) |
dragend | 拖動(dòng)結(jié)束后觸發(fā) 1 次 |
- 可釋放目標(biāo)的事件
| 事件 | 觸發(fā)時(shí)機(jī) |
|---|---|
dragenter | 拖拽元素進(jìn)入可釋放目標(biāo)時(shí)執(zhí)行 1 次 |
dragover | 拖拽元素進(jìn)入可釋放目標(biāo)時(shí)觸發(fā)多次(100毫秒觸發(fā)一次) |
drop | 拖拽元素進(jìn)入可釋放目標(biāo)內(nèi)釋放時(shí)(設(shè)置了dragover此事件才會(huì)生效) |
可放置目標(biāo)
dragenter 或 dragover事件可用于表示有效的放置目標(biāo),也就是被拖拽元素可能放置的地方。
設(shè)置允許被被放置還需要阻止 dragenter 和 dragover 事件的默認(rèn)處理。
<div ondragenter="event.preventDefault()">
做一個(gè)例子來理解 可放置目標(biāo):
<div id="div"
ondragover="dragover(event)"
ondragenter="dragenter(event)""
>
我是可放置目標(biāo)
</div>
<p id="drag"
draggable="true"
ondragstart="dragstart(event)"
ondragend="dragend(event)"
>
我是拖拽元素,把我拖拽到上面去
</p>#div {
width:300px;
height:80px;
padding:10px;
border:1px solid gray;
}let targetDom
function dragstart(e) {
}
function dragenter(e) {
e.preventDefault()
targetDom = e.target
}
function dragover(e) {
e.preventDefault()
e.dataTransfer.dropEffect = 'move'
}
function dragend(e) {
e.preventDefault()
targetDom.appendChild(e.target)
}DataTransfer 對(duì)象
DataTransfer 對(duì)象用于保存拖拽過程中的數(shù)據(jù)、設(shè)置拖動(dòng)類型等。在所有拖動(dòng)事件 event的 dataTransfer 屬性上都能獲取到 DataTransfer 對(duì)象。
dataTransfer.dropEffect設(shè)置拖拽的操作類型。值必須是none, copy, link 或 move。
function dragover(e) {
e.preventDefault()
e.dataTransfer.dropEffect = 'move'
}傳遞數(shù)據(jù) (不建議使用這種方法,可能有兼容問題)只能在drop事件中接收(測試的時(shí)候在其他拖拽事件中獲取不到)
function dragstart(e, index) {
e.stopPropagation()
e.dataTransfer.dropEffect = 'move'
// 傳數(shù)據(jù)
e.dataTransfer.setData('text/plain', '111111111')
}
function drop(e) {
// 接收數(shù)據(jù)
console.log(e.dataTransfer.getData('text/plain'));
}Vue3 中拖拽排序
根據(jù)上面描述的屬性和事件在 Vue3 編寫一個(gè)拖拽排序的列表大概分下面幾個(gè)步驟:
- 創(chuàng)建一個(gè)列表,遍歷渲染到頁面
- 列表項(xiàng)添加
draggable="true" - 列表項(xiàng)添加事件
dragstartdragenterdragenddragover - 在
dragenter事件中,需要傳入列表項(xiàng)的下標(biāo),實(shí)時(shí)進(jìn)行元素的排序。排序的核心邏輯也是在dragenter中 - 代碼執(zhí)行的邏輯是:列表項(xiàng)拖拽到可放置目標(biāo)時(shí),將該拖拽的元素從原位置刪除,再將拖拽的元素插入到當(dāng)前可放置目標(biāo)的位置
代碼如下:
<template>
<div>
<div
class="item"
v-for="(item, i) in drag.list"
:key="item.id"
draggable="true"
@dragstart="dragstart($event, i)"
@dragenter="dragenter($event, i)"
@dragend="dragend"
@dragover="dragover"
>
{{ item.name }}
</div>
</div>
</template>
<script setup>
import { reactive } from 'vue'
const drag = reactive({
list: [
{ name: 'a', id: 1 },
{ name: 'b', id: 2 },
{ name: 'c', id: 3 },
{ name: 'd', id: 4 },
{ name: 'e', id: 5 },
]
})
let dragIndex = 0
function dragstart(e, index) {
e.stopPropagation()
dragIndex = index
setTimeout(() => {
e.target.classList.add('moveing')
},0)
}
function dragenter(e, index) {
e.preventDefault()
// 拖拽到原位置時(shí)不觸發(fā)
if (dragIndex !== index) {
const source = drag.list[dragIndex];
drag.list.splice(dragIndex, 1);
drag.list.splice(index, 0, source);
// 更新節(jié)點(diǎn)位置
dragIndex = index
}
}
function dragover(e) {
e.preventDefault()
e.dataTransfer.dropEffect = 'move'
}
function dragend(e) {
e.target.classList.remove('moveing')
}
</script>
<style lang="scss" scoped>
.item {
width: 200px;
height: 40px;
line-height: 40px;
// background-color: #f5f6f8;
background-color: skyblue;
text-align: center;
margin: 10px;
color: #fff;
font-size: 18px;
}
.container {
position: relative;
padding: 0;
}
.moveing {
opacity: 0;
}
</style>此時(shí)是這樣的效果:

這樣就在 Vue3 中實(shí)現(xiàn)了拖拽排序。還可以利用 Vue 的<TransitionGroup> 內(nèi)置組件,添加動(dòng)畫效果,讓元素的過渡不會(huì)很生硬。
添加代碼:
<template>
<div>
<TransitionGroup name="list" tag="div" class="container">
<div
class="item"
v-for="(item, i) in drag.list"
:key="item.id"
draggable="true"
@dragstart="dragstart($event, i)"
@dragenter="dragenter($event, i)"
@dragend="dragend"
@dragover="dragover"
>
{{ item.name }}
</div>
</TransitionGroup>
</div>
</template>
<style lang="scss" scoped>
.list-move, /* 對(duì)移動(dòng)中的元素應(yīng)用的過渡 */
.list-enter-active,
.list-leave-active {
transition: all 0.2s ease;
}
</style>效果如下:

sortable.js
接下來在 Element Plus 組件庫中使用 sortable.js 進(jìn)行表格排序。
Element Plus 是基于 Vue3 的常用的開源組件庫
安裝 sortable.js
npm i sortablejs -S
使用 el-table 組件,寫一個(gè)表格:
<template>
<div>
<el-table :data="tableData" id="dragTable" border style="width: 800px;">
<el-table-column prop="date" label="Date" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="address" label="Address" />
</el-table>
</div>
</template>
<script setup>
const tableData = [
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Cilly',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Linda',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-01',
name: 'John',
address: 'No. 189, Grove St, Los Angeles',
},
]
</script>現(xiàn)在就有了一個(gè)表格:

然后導(dǎo)入 sortable.js:
<script setup>
import Sortable from 'sortablejs'
import { onMounted } from 'vue'
function setSort() {
const el = document.querySelector('#dragTable table tbody')
new Sortable(el, {
sort: true,
ghostClass: 'sortable-ghost',
onEnd: (e) => {
const targetRow = tableData.splice(e.oldIndex, 1)[0]
tableData.splice(e.newIndex, 0, targetRow)
console.log(tableData)
},
})
}
onMounted(() => {
setSort()
})
const tableData = [
// ...
]
</script>在 onMounted 中,也就是組件掛載完成之后,實(shí)例化 Sortable(),傳入要進(jìn)行拖拽排序的節(jié)點(diǎn) el 和其它一些配置參數(shù)。現(xiàn)在可以進(jìn)行拖拽了。

有些時(shí)候,可能需要按住拖動(dòng)圖標(biāo)才可以進(jìn)行拖動(dòng),需要添加 handle 配置,并指定對(duì)應(yīng)的樣式名。
<el-table :data="tableData" id="dragTable" border style="width: 600px; margin: 20px">
<!-- ...... 省略代碼 -->
<el-table-column label="操作" width="100">
<template #default>
<div class="handle-drag">
<el-icon>
<Sort />
</el-icon>
</div>
</template>
</el-table-column>
</el-table>上面代碼將表格添加了一個(gè)操作列,并將操作列的圖標(biāo)設(shè)置一個(gè)樣式類。下面的配置表示只有包含.handle-drag 樣式的元素才可以被拖動(dòng)。其他位置不能被拖動(dòng)。
new Sortable(el, {
// ...
handle: '.handle-drag',
// ...
})
vuedraggable
vue.draggable.next 是 Vue3 的拖拽組件,是基于 Sortable.js 實(shí)現(xiàn)的??梢杂糜谕献Я斜?、菜單、工作臺(tái)、選項(xiàng)卡等常見的場景。
安裝:
npm i -S vuedraggable@next
使用:
<script setup>
import draggable from 'vuedraggable'
import { reactive } from 'vue'
const state = reactive({
list1: [1, 2, 3, 4],
list2: ['a', 'b', 'c', 'd'],
})
function onStart() {}
function onEnd() {
console.log(state)
}
</script>先導(dǎo)入 draggable并定義一些基礎(chǔ)數(shù)據(jù)。
<template>
<div style="margin-left: 30px;">
<draggable
:list="state.list1"
:force-fallback="true"
chosen-class="chosen"
animation="300"
@start="onStart"
@end="onEnd"
>
<template #item="{ element }">
<div class="item">
{{ element }}
</div>
</template>
</draggable>
</div>
</template>其中 @start 和 @end 為拖拽開始和結(jié)束時(shí)的事件。chosen-class 為拖拽時(shí)的樣式。

為組件設(shè)置相同的 group 屬性,可以實(shí)現(xiàn)在不同的塊之間拖拽。
<draggable group="group" :list="state.list1" >
<template #item="{ element }">
<div class="item bck1">
{{ element }}
</div>
</template>
</draggable>
<draggable group="group" :list="state.list2" >
<template #item="{ element }">
<div class="item bck2">
{{ element }}
</div>
</template>
</draggable>
相關(guān)地址
到此這篇關(guān)于Vue3中列表拖拽排序的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)Vue3 列表拖拽排序內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue.js 實(shí)現(xiàn)評(píng)價(jià)五角星組件的實(shí)例代碼
這篇文章主要介紹了vue.js 實(shí)現(xiàn)評(píng)價(jià)五角星組件的實(shí)例代碼,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-08-08
vue3自定義指令自動(dòng)獲取節(jié)點(diǎn)的width和height代碼示例
這篇文章主要介紹了如何使用ResizeObserver監(jiān)聽組件的寬度和高度,并將其封裝成一個(gè)指令以便全局或局部使用,ResizeObserver可以監(jiān)聽元素的多個(gè)尺寸屬性,如top、bottom、left等,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-11-11
vue微信分享出來的鏈接點(diǎn)開是首頁問題的解決方法
這篇文章主要為大家詳細(xì)介紹了vue微信分享出來的鏈接點(diǎn)開是首頁問題的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-11-11
vue項(xiàng)目如何配置public靜態(tài)資源路徑訪問
這篇文章主要介紹了vue項(xiàng)目如何配置public靜態(tài)資源路徑訪問方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11
vue實(shí)現(xiàn)仿淘寶結(jié)賬頁面實(shí)例代碼
本文是小編給大家分享的vue實(shí)現(xiàn)仿淘寶結(jié)賬頁面實(shí)例代碼,主要功能是仿照淘寶頁面的結(jié)算購物車商品時(shí)自動(dòng)算出合計(jì)價(jià)格的頁面,具體實(shí)例代碼大家參考下本文2017-11-11
詳解element-ui日期時(shí)間選擇器的日期格式化問題
這篇文章主要介紹了詳解element-ui日期時(shí)間選擇器的日期格式化問題,本文用到了DateTimePicker來選擇日期時(shí)間,但是在將數(shù)據(jù)傳回后臺(tái)的過程中遇到了一些令人頭疼的問題,有興趣的一起來了解一下2019-04-04

