需求描述:
最近在一个需求中遇到一个需要实现一个拖拽某行数据到一个列表中,从而在对应的列表中展示该被拖拽行的数据!
实现:
实现过程全程没有使用什么插件,但是有很多插件可以使用。作者实现的过程是使用html5的新特性---draggable!而且可以看到此特性兼容性较好。
于是乎,我写了个大概的demo如下:
这里面其实还用了 事件代理,将@dragstart事件放在父盒子中,防止生成的的li标签过多,注册的事件监听过多。
<section class="drag-demo">
<div class="can-drag-list">
<ul
@dragstart="onDragStart"
@dragend="onDragEnd"
@dragenter="onDragEnter"
>
<li
v-for="drag in dragList"
:key="drag.id"
:data-id="drag.id"
draggable
>
{{ drag.name }}
</li>
</ul>
</div>
<div
class="drag-into-box"
ref="dropTo"
@drop="onDrop"
@dragover="onDragOver"
/>
</section>
写了很多盒子层级,说明这个是可以在多级中嵌套使用,不会影响。js代码如下
export default {
name: 'Drag',
data() {
return {
storageLi: {
storageText: '',
storageId: null,
dropDownId: null
},
dragList: [{
id: 1,
name: '名称1'
}, {
id: 2,
name: '名称2'
}, {
id: 3,
name: '世界大会就'
}, {
id: 4,
name: '四大'
}, {
id: 5,
name: '反对法'
}]
};
},
created() {},
methods: {
// 开始拖动的事件
onDragStart (event) {
if(event.target.nodeName !== 'LI') return
console.log("开始传递", event.target.innerText)
this.storageLi.storageText = event.target.innerText
this.storageLi.storageId = event.target.getAttribute('data-id');
},
// 阻止默认行为
onDragOver (event) {
event.preventDefault()
},
// 拖拽进去对应dom放下的事件
onDrop (event) {
let newElement = document.createElement('div')
newElement.innerText = this.storageLi.storageText;
const dropToDom = this.$refs.dropTo
dropToDom.appendChild(newElement)
},
}
}
在这里面需要注意的是:拖拽放下的事件需要使用@dragover触发的事件中阻止默认行为,否则并不会触发@drop事件。我们可以看到拖拽放下的事件中用到了生成dom并插入的方法,但是我们用了vue开发其实可以不用这样,可以定义一个展示数组,通过操作这个数组的行为来在模板中动态展示列表。
列表拖拽排序:
当然,这个可以实现操作的并不完全仅限于拖到另一个dom,在开发中,我们也会有需求可以实现拖拽排序之类的功能。代码如下
// 自己列表内拖拽结束事件触发
onDragEnd (event) {
if(event.target.nodeName !== 'LI') return
// 处理数组位置对调
let dragList = this.dragList
const sourceId = this.storageLi.storageId
const targetId = this.storageLi.dropDownId
// 查找源元素和目标元素的索引位置
const sourceIndex = dragList.findIndex(item => item.id == sourceId);
const targetIndex = dragList.findIndex(item => item.id == targetId);
// 如果找到了源元素和目标元素的索引位置
if (sourceIndex !== -1 && targetIndex !== -1) {
// 如果源元素在目标元素之前,将源元素插入目标元素之后的位置
if (sourceIndex < targetIndex) {
dragList.splice(targetIndex + 1, 0, dragList[sourceIndex]);
dragList.splice(sourceIndex, 1);
}
// 如果源元素在目标元素之后,将源元素插入目标元素之后的位置,再删除原来的源元素位置
else if (sourceIndex > targetIndex) {
dragList.splice(targetIndex, 0, dragList[sourceIndex]);
dragList.splice(sourceIndex + 1, 1);
}
}
console.log(dragList, 'dragList')
this.$set(this, 'dragList', dragList)
},
// 拖拽进入的事件
onDragEnter (event) {
const target = event.target;
if(target.nodeName !== 'LI') return
// 通过 target.getAttribute 获取元素上的自定义属性值
const dropDownId = target.getAttribute('data-id');
this.storageLi.dropDownId = dropDownId
}