在我的项目中使用过vue.draggable这个插件,用来做拖拽,它是封装Sortable这个插件的。
各位大爷们,传送门我放这:
面试官:说说你觉得最有挑战的项目?
我:巴拉巴拉,我用了一个vue.draggable做了拖拽效果
面试官:哦,看过其源码么?说说这个vue.draggable的拖拽原理是什么?
我:本质上是利用H5的drag属性来做的,一个数组,改变其的顺序,巴拉巴拉
面试官:那他是怎么实现改变顺序的?
我:o((⊙﹏⊙))o (面对自己不知道的问题坐如针毡,哎,找个地缝进去算了)
我做项目没有看过其源码,说起它的原理来也是靠猜的,今天我网上去下载下源码来,看了半个小时,两眼一抹黑,更懵了。
然后我就网上去找拖拽改变顺序的原理,万能的搜索引擎,我找到了这个大佬写的,拿来看了,方才如梦初醒。各位大爷,链接给你们放下面了。
我把他的代码拷贝下来,运行了下,看了看,自己照着写了写,谢谢作者大佬。谢谢大哥精简的代码,让我明白了其实现原理。厉害,厉害,谢谢!谢谢!谢谢!
如果有侵权,我立即删了
虽然赶不上人家,不过,胡适说过:
怕什么真理无穷,进一寸有一寸的欢喜。
我之前想得很复杂,如何判断拖拽后的元素位于什么位置,如何去改变顺序,想来想去,更没有办法下笔了。各位大爷们,完整渣渣代码我贴在最后,我把我觉得核心的拿出来讲讲。还是带着问题去看吧。
- 靠哪个属性进行拖拽?
- 怎么判断拖拽到哪个位置了?
- 怎么交换顺序?
1、靠哪个属性值进行拖拽
利用h5的拖拽属性draggable,配合相应的事件ondragstart和ondragover来实现拖拽。
2、怎么判断拖拽到哪个位置了
首先被拖拽的目标元素dragObj,经过每一个子元素item时候,都会触发其dragover事件。
可以利用一个函数,来计算item在父元素的的index值。经过该元素时候,计算item和dragObj的index值,然后利用dom的insertBefore来把dragObj插入到该元素上面或者下面。
判断index大小主要用来判断是在item的上面插入还是下面插入。
3、怎么交换顺序
交换顺序主要是使用了dom的insertBefore,它是插入和改变dom节点的api,各位大爷,链接我放这:
www.w3school.com.cn/jsref/met_n…
所以,说了那么多,那个获取index的函数怎么写?
// 获取元素所在数组的当前index
function _index(el) {
var index = 0;
if (!el || !el.parentNode) {
return -1;
}
// 反复寻找它有几个前兄弟元素
// previousElementSibling 属性返回指定元素的前一个兄弟元素,循环遍历之前的所有兄弟节点
while (el && (el = el.previousElementSibling)) {
// console.log(el);
index++;
// console.log('--while---',el,index);
}
// console.log(index)
return index;
}
最后,
关键:一个属性,一个变量,两个事件,一个函数,一个dom的api。
一个属性:draggable="true"
一个变量:用来存放被拖拽的元素dragObj
两个事件:ondragstart,ondragover
一个函数:计算元素处于父元素的子元素数组里面的哪个index
一个dom的api:insertBefore
思路:
1、给被拖拽的子元素加上draggable="true"属性,
2、父元素绑定ondragstart和ondragover事件,
3、dragstart的时候,把被拖拽的元素赋值存起来为dragObj,
4、每个被经过的子元素target都会有dragover事件,
5、在dragover事件里,利用获取index的函数,判断被拖拽元素dragObj和target元素的index的大小
6、在taget的nextSibing或者taget自己利用insertBefore插入dragObj。
最后:
这个版本是乞丐版本,各位大爷们将就下哈。我后续还会接着啃vue.draggable源码,给各位看官用口水话讲出来,方便理解,我还会继续更新拖拽方面的内容,这只是一个开始。
最后渣渣代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.item {
width: 100%;
height: 50px;
border: 1px solid red;
}
</style>
</head>
<body>
<div id="ordered-container"> </div>
<script>
var data = [
{ label: "Item 1" },
{ label: "Item 2" },
{ label: "Item 3" },
{ label: "Item 4" },
{ label: "Item 5" },
{ label: "Item 6" }]
setupOrderedContainers();
function setupOrderedContainers() {
// 先渲染列表
var container = document.querySelector("#ordered-container");
var html = '';
data.forEach(item => {
html+=`<div class="item" draggable="true">
${item.label}
</div>`;
})
container.innerHTML = html;
var drag = null;
// 拖拽开始,赋值
container.ondragstart = function (event) {
drag = event.target;
}
// 拖拽过程中,插入元素
container.ondragover = function (event) {
var target = event.target;
// console.log('--ondragover---', _index(drag), _index(target))
if(_index(drag) < _index(target)) {
// 向下拖拽
// console.log('向下拖拽');
target.parentNode.insertBefore(drag, target.nextSibing);
} else {
// console.log('向上拖拽');
target.parentNode.insertBefore(drag, target);
}
}
}
// 获取元素所在数组的当前index
function _index(el) {
var index = 0;
if (!el || !el.parentNode) {
return -1;
}
// 反复寻找它有几个前兄弟元素
// previousElementSibling 属性返回指定元素的前一个兄弟元素,循环遍历之前的所有兄弟节点
while (el && (el = el.previousElementSibling)) {
// console.log(el);
index++;
// console.log('--while---',el,index);
}
// console.log(index)
return index;
}
</script>
</body>
</html>