vue實(shí)現(xiàn)前端拖拽div位置交換的方法詳解
一、場(chǎng)景描述
類似備忘錄,點(diǎn)擊添加按鈕,多一條條目。然后手動(dòng)拖拽條目可以更換條目之前的位置。
二、問(wèn)題拆解
這里可以分成兩個(gè)問(wèn)題,第一個(gè)是添加,第二個(gè)是拖拽。
添加的實(shí)現(xiàn):vue技術(shù)像一個(gè)前端頁(yè)面的數(shù)據(jù)管理器,它里面的 “v-for”列表渲染指令支持當(dāng)列表數(shù)據(jù)增加的時(shí)候?qū)崿F(xiàn)重新渲染增加一個(gè)條目。
拖拽的實(shí)現(xiàn):拖拽事件,開(kāi)始的時(shí)候需要記錄所拖拽的目標(biāo),拖拽經(jīng)過(guò)的實(shí)現(xiàn)交換。
三、知識(shí)背景
3.1 vue拖拽事件

在這里,我們使用的是開(kāi)始拖拽事件和在有效區(qū)域移動(dòng)事件,為的是得到所拖拽的標(biāo)簽元素以及被拖動(dòng)標(biāo)簽元素所要到達(dá)的原有標(biāo)簽元素的位置。
下面舉例一下拖拽事件怎么用。
首先需要開(kāi)始標(biāo)簽拖拽功能:draggable=“true” ,再添加拖拽事件。
<div class="task" draggable="true" @dragstart.self = "ondragstart($event)" @dragover.self = "ondragover($event)"> </div>
3.2 js獲得同級(jí)元素節(jié)點(diǎn)
ele.previousSibling ele.previousElementSibling 獲取同級(jí)的上下級(jí),(前一個(gè)標(biāo)簽元素和后一個(gè)標(biāo)簽元素) ele.nextSibling ele.nextElementSibling

<input id="a5" type="button" onclick="console.log('previousSibling是'+this.previousSibling);" value="e" />
<!-- 這是個(gè)text對(duì)象,因?yàn)樵谶@個(gè)標(biāo)簽元素前面是一個(gè)換行符 -->
<input id="a6" type="button" onclick="console.log(this.previousSibling);" value="e" />
<input id="a7" type="button" onclick="console.log('previousElementSibling是'+this.previousElementSibling);" value="e" />
<!-- 這是個(gè)標(biāo)簽元素,因?yàn)樵谶@個(gè)js代碼所取的是一個(gè)前一個(gè)標(biāo)簽對(duì)象 -->
<input id="a8" type="button" onclick="console.log(this.previousElementSibling);" value="e" />

四、場(chǎng)景實(shí)現(xiàn)
添加的實(shí)現(xiàn):就是用vue中的v-for指令。點(diǎn)擊按鈕之后,在列表中加一個(gè)列表元素,就會(huì)重新渲染。
拖拽的實(shí)現(xiàn):這里有兩種可能性,一個(gè)是如果是往前拖拽,則所要拖拽元素放在目標(biāo)元素之前;如果是往后拖拽,則所要拖拽元素放在目標(biāo)元素之后。
因此需要判斷目標(biāo)元素是否是前面的元素。
isPreviousElements(sourse, target){
//這里是判斷前面是否還有元素,sourse是不是第一個(gè)元素
if(!sourse.previousElementSibling){
return false;
}
//這里是判斷
if(target.isEqualNode(sourse.previousElementSibling)){
return true;
}
return this.isPreviousElements(sourse.previousElementSibling, target)
},
然后把標(biāo)簽元素放入到目標(biāo)元素之前或之后。
完整代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>todayTask</title>
<script src="../js/vue.js"></script>
<style>
.task{
width: 300px;
height: 50px;
list-style-type:decimal;
list-style-position:inside;
cursor: grab;
position: absolute;
transition: top;
transition-duration: 0.6s;
}
.taskList{
position: relative;
display: flex;
flex-direction: column;
}
.addTask{
display: block;
}
</style>
</head>
<body>
<div class="container">
<button class="addTask" @click="addTask">添加任務(wù)</button>
<div class="taskList">
<div class="task" draggable="true" @dragstart.self = "ondragstart($event)" @dragover.self = "ondragover($event)" v-for="task in tasks" :key="task.id">
<span>
{{task.id}}
<input type="text" v-model="task.task">
</span>
</div>
</div>
</div>
</body>
<script>
document.body.addEventListener("dragover",function(ev){
ev.preventDefault();
})
new Vue({
el:".container",
data:{
tasks:[
{id:1,task:"",isDone:false}
],
dragDiv:"",
isMoving:false
},
methods:{
addTask(){
this.tasks.push({id:this.tasks.length+1,task:"",isDone:false})
var taskList = document.getElementsByClassName("taskList")[0];
taskList.style.height = this.tasks.length*50+"px"
},
sortDiv(divs){
for(var i=0;i<divs.length;i++){
divs[i].style.top = i*50 + "px";
}
},
isPreviousElements(sourse, target){
//返回上一節(jié)點(diǎn)
if(!sourse.previousElementSibling){
return false;
}
if(target.isEqualNode(sourse.previousElementSibling)){
return true;
}
return this.isPreviousElements(sourse.previousElementSibling, target)
},
ondragstart(ev){
this.dragDiv = ev.target;
console.log("dragstart");
},
ondragover(ev){
overDrag = ev.target;
console.log(overDrag.isEqualNode(this.dragDiv));
console.log(this.isMoving);
if(this.isMoving || overDrag.isEqualNode(this.dragDiv)){
return;
}
//判斷是否是前一個(gè)標(biāo)簽元素
if(this.isPreviousElements(overDrag,this.dragDiv)){
overDrag.parentNode.insertBefore(this.dragDiv,overDrag.nextElementSibling);
}else{
overDrag.parentNode.insertBefore(this.dragDiv,overDrag);
}
this.isMoving = true;
const self = this;
var st = setTimeout(function(){
self.isMoving = false;
clearTimeout(st);
},600);
this.sortDiv(document.querySelectorAll(".task"));
}
},
created(){
//設(shè)置ul的盒子高度
var taskList = document.getElementsByClassName("taskList")[0];
taskList.style.height = this.tasks.length*50+"px";
//設(shè)置每一個(gè)item的上邊緣top
this.sortDiv(document.querySelectorAll(".task"));
},
updated(){
this.sortDiv(document.querySelectorAll(".task"));
}
})
</script>
</html>總結(jié)
到此這篇關(guān)于vue實(shí)現(xiàn)前端拖拽div位置交換的文章就介紹到這了,更多相關(guān)vue前端拖拽div位置交換內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue3項(xiàng)目如何使用樣式穿透修改elementUI默認(rèn)樣式
這篇文章主要介紹了vue3項(xiàng)目使用樣式穿透修改elementUI默認(rèn)樣式,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-03-03
解決v-if 與 v-for 同時(shí)使用報(bào)錯(cuò)的問(wèn)題
在進(jìn)行項(xiàng)目開(kāi)發(fā)的時(shí)候因?yàn)樵谝粋€(gè)標(biāo)簽上同時(shí)使用了v-for和v-if兩個(gè)指令導(dǎo)致的報(bào)錯(cuò),遇到這個(gè)問(wèn)題如何解決呢?下面小編給大家?guī)?lái)了關(guān)于v-if 與 v-for 使用報(bào)錯(cuò)問(wèn)題分析及解決方法,一起看看吧2022-03-03
vue3中watch和watchEffect實(shí)戰(zhàn)梳理
這篇文章主要介紹了vue3中watch和watchEffect實(shí)戰(zhàn)梳理,watch和watchEffect都是vue3中的監(jiān)聽(tīng)器,但是在寫(xiě)法和使用上是有區(qū)別的。下文介紹他們之間的方法及區(qū)別,需要的朋友可以參考一下2022-07-07
axios的interceptors多次執(zhí)行問(wèn)題解決
這篇文章主要為大家介紹了axios中interceptors多次執(zhí)行問(wèn)題解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06
關(guān)于Nuxt的五種渲染模式的差異和使用場(chǎng)景全解析
這篇文章主要介紹了關(guān)于Nuxt的五種渲染模式的差異和使用場(chǎng)景全解析,在過(guò)去傳統(tǒng)開(kāi)發(fā)中,頁(yè)面渲染任務(wù)是由服務(wù)端完成的,那么Nuxt是如何渲染的呢,需要的朋友可以參考下2023-04-04
Vue中$on和$emit的實(shí)現(xiàn)原理分析
這篇文章主要介紹了Vue中$on和$emit的實(shí)現(xiàn)原理分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05
解決VUE的對(duì)話框el-dialog點(diǎn)擊外部消失問(wèn)題
這篇文章主要介紹了解決VUE的對(duì)話框el-dialog點(diǎn)擊外部消失問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-02-02
前端Vue.js實(shí)現(xiàn)json數(shù)據(jù)導(dǎo)出到doc
這篇文章主要介紹了前端Vue.js實(shí)現(xiàn)json數(shù)據(jù)導(dǎo)出到doc,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09

