vue實(shí)現(xiàn)前端拖拽div位置交換的方法詳解
一、場(chǎng)景描述
類(lèi)似備忘錄,點(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-03vue3中watch和watchEffect實(shí)戰(zhàn)梳理
這篇文章主要介紹了vue3中watch和watchEffect實(shí)戰(zhàn)梳理,watch和watchEffect都是vue3中的監(jiān)聽(tīng)器,但是在寫(xiě)法和使用上是有區(qū)別的。下文介紹他們之間的方法及區(qū)別,需要的朋友可以參考一下2022-07-07axios的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-04Vue中$on和$emit的實(shí)現(xiàn)原理分析
這篇文章主要介紹了Vue中$on和$emit的實(shí)現(xiàn)原理分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05解決VUE的對(duì)話(huà)框el-dialog點(diǎn)擊外部消失問(wèn)題
這篇文章主要介紹了解決VUE的對(duì)話(huà)框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