Vue3中列表拖拽排序的實(shí)現(xiàn)示例
本文主要內(nèi)容分三個(gè)部分。先寫(xiě)了在 Vue3 中利用 HTML5
的 draggable
屬性手寫(xiě)實(shí)現(xiàn)列表拖拽排序的功能。接下來(lái)記錄了在 Element Plus
組件庫(kù)中結(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 | 開(kāi)始拖拽時(shí)執(zhí)行 1 次 |
drag | 拖拽開(kāi)始后多次觸發(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è)例子來(lái)理解 可放置目標(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ì)象用于保存拖拽過(guò)程中的數(shù)據(jù)、設(shè)置拖動(dòng)類(lèi)型等。在所有拖動(dòng)事件 event
的 dataTransfer
屬性上都能獲取到 DataTransfer
對(duì)象。
dataTransfer.dropEffect
設(shè)置拖拽的操作類(lèi)型。值必須是none
, copy
, link
或 move
。
function dragover(e) { e.preventDefault() e.dataTransfer.dropEffect = 'move' }
傳遞數(shù)據(jù) (不建議使用這種方法,可能有兼容問(wèn)題)只能在drop
事件中接收(測(cè)試的時(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 編寫(xiě)一個(gè)拖拽排序的列表大概分下面幾個(gè)步驟:
- 創(chuàng)建一個(gè)列表,遍歷渲染到頁(yè)面
- 列表項(xiàng)添加
draggable="true"
- 列表項(xiàng)添加事件
dragstart
dragenter
dragend
dragover
- 在
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à)效果,讓元素的過(guò)渡不會(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)用的過(guò)渡 */ .list-enter-active, .list-leave-active { transition: all 0.2s ease; } </style>
效果如下:
sortable.js
接下來(lái)在 Element Plus
組件庫(kù)中使用 sortable.js
進(jìn)行表格排序。
Element Plus 是基于 Vue3 的常用的開(kāi)源組件庫(kù)
安裝 sortable.js
npm i sortablejs -S
使用 el-table
組件,寫(xiě)一個(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ù)?,F(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è)樣式類(lèi)。下面的配置表示只有包含.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)卡等常見(jiàn)的場(chǎ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
為拖拽開(kāi)始和結(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-08vue3自定義指令自動(dòng)獲取節(jié)點(diǎn)的width和height代碼示例
這篇文章主要介紹了如何使用ResizeObserver監(jiān)聽(tīng)組件的寬度和高度,并將其封裝成一個(gè)指令以便全局或局部使用,ResizeObserver可以監(jiān)聽(tīng)元素的多個(gè)尺寸屬性,如top、bottom、left等,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-11-11vue微信分享出來(lái)的鏈接點(diǎn)開(kāi)是首頁(yè)問(wèn)題的解決方法
這篇文章主要為大家詳細(xì)介紹了vue微信分享出來(lái)的鏈接點(diǎn)開(kāi)是首頁(yè)問(wèn)題的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-11-11vuex中五大屬性和使用說(shuō)明(包括輔助函數(shù))
這篇文章主要介紹了vuex中五大屬性和使用說(shuō)明(包括輔助函數(shù)),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05vue項(xiàng)目如何配置public靜態(tài)資源路徑訪問(wèn)
這篇文章主要介紹了vue項(xiàng)目如何配置public靜態(tài)資源路徑訪問(wèn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11vue實(shí)現(xiàn)仿淘寶結(jié)賬頁(yè)面實(shí)例代碼
本文是小編給大家分享的vue實(shí)現(xiàn)仿淘寶結(jié)賬頁(yè)面實(shí)例代碼,主要功能是仿照淘寶頁(yè)面的結(jié)算購(gòu)物車(chē)商品時(shí)自動(dòng)算出合計(jì)價(jià)格的頁(yè)面,具體實(shí)例代碼大家參考下本文2017-11-11詳解element-ui日期時(shí)間選擇器的日期格式化問(wèn)題
這篇文章主要介紹了詳解element-ui日期時(shí)間選擇器的日期格式化問(wèn)題,本文用到了DateTimePicker來(lái)選擇日期時(shí)間,但是在將數(shù)據(jù)傳回后臺(tái)的過(guò)程中遇到了一些令人頭疼的問(wèn)題,有興趣的一起來(lái)了解一下2019-04-04vue過(guò)渡和animate.css結(jié)合使用詳解
本篇文章主要介紹了vue過(guò)渡和animate.css結(jié)合使用詳解,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06