【vue】 vue实现前端拖拽div位置交换

一、场景描述

类似备忘录,点击添加按钮,多一条条目。然后手动拖拽条目可以更换条目之前的位置。

二、问题拆解

这里可以分成两个问题,第一个是添加,第二个是拖拽。

添加的实现:vue技术像一个前端页面的数据管理器,它里面的 "v-for"列表渲染指令支持当列表数据增加的时候实现重新渲染增加一个条目。

拖拽的实现:拖拽事件,开始的时候需要记录所拖拽的目标,拖拽经过的实现交换。

三、知识背景

3.1 vue拖拽事件

在这里,我们使用的是开始拖拽事件和在有效区域移动事件,为的是得到所拖拽的标签元素以及被拖动标签元素所要到达的原有标签元素的位置。

下面举例一下拖拽事件怎么用。

首先需要开始标签拖拽功能:draggable="true" ,再添加拖拽事件。

html 复制代码
<div class="task" draggable="true" @dragstart.self = "ondragstart($event)" @dragover.self = "ondragover($event)">
</div>

3.2 js获得同级元素节点

js 复制代码
ele.previousSibling ele.previousElementSibling 获取同级的上下级,(前一个标签元素和后一个标签元素)
ele.nextSibling ele.nextElementSibling
html 复制代码
<input id="a5" type="button" onclick="console.log('previousSibling是'+this.previousSibling);" value="e" />
<!-- 这是个text对象,因为在这个标签元素前面是一个换行符 -->
<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" />
<!-- 这是个标签元素,因为在这个js代码所取的是一个前一个标签对象 -->
<input id="a8" type="button" onclick="console.log(this.previousElementSibling);" value="e" />

四、场景实现

添加的实现:就是用vue中的v-for指令。点击按钮之后,在列表中加一个列表元素,就会重新渲染。

拖拽的实现:这里有两种可能性,一个是如果是往前拖拽,则所要拖拽元素放在目标元素之前;如果是往后拖拽,则所要拖拽元素放在目标元素之后。

因此需要判断目标元素是否是前面的元素。

javascript 复制代码
isPreviousElements(sourse, target){
    //这里是判断前面是否还有元素,sourse是不是第一个元素
    if(!sourse.previousElementSibling){
        return false;
    }
    //这里是判断
    if(target.isEqualNode(sourse.previousElementSibling)){
        return true;
    }
    return this.isPreviousElements(sourse.previousElementSibling, target)
},

然后把标签元素放入到目标元素之前或之后。

完整代码

html 复制代码
<!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">添加任务</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){
                //返回上一节点
                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;
                }
                //判断是否是前一个标签元素
                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(){
            //设置ul的盒子高度
            var taskList = document.getElementsByClassName("taskList")[0];
            taskList.style.height = this.tasks.length*50+"px";
            //设置每一个item的上边缘top
            this.sortDiv(document.querySelectorAll(".task"));
        },
        updated(){
            this.sortDiv(document.querySelectorAll(".task"));
        }
    })
 
</script>
</html>
相关推荐
zhougl99640 分钟前
html处理Base文件流
linux·前端·html
花花鱼44 分钟前
node-modules-inspector 可视化node_modules
前端·javascript·vue.js
HBR666_1 小时前
marked库(高效将 Markdown 转换为 HTML 的利器)
前端·markdown
careybobo2 小时前
海康摄像头通过Web插件进行预览播放和控制
前端
TDengine (老段)3 小时前
TDengine 中的关联查询
大数据·javascript·网络·物联网·时序数据库·tdengine·iotdb
杉之4 小时前
常见前端GET请求以及对应的Spring后端接收接口写法
java·前端·后端·spring·vue
喝拿铁写前端4 小时前
字段聚类,到底有什么用?——从系统混乱到结构认知的第一步
前端
再学一点就睡4 小时前
大文件上传之切片上传以及开发全流程之前端篇
前端·javascript
木木黄木木5 小时前
html5炫酷图片悬停效果实现详解
前端·html·html5
请来次降维打击!!!5 小时前
优选算法系列(5.位运算)
java·前端·c++·算法