前言
在业务中列表拖拽排序是比较常见的需求,常见的JS拖拽库有Sortable.js,Vue.Draggable等,大多数同学遇到这种需求也是更多的求助于这些JS库,其实,使用HTML原生的拖放事件来实现拖拽排序并不复杂,结合Vue的transition-group
,还能快速的给排序添加过渡动画。
HTML5拖放API
设置元素为可拖放
让一个元素能被拖放需要设置 draggable
属性为true
(文本、图片和链接的draggable
默认就是true
)
<div draggable="true">能被拖放的元素</div>
拖放事件
拖放涉及到两种元素,一种是被拖拽元素(源对象),一种是放置区元素(目标对象)。如下图所示,按住A元素往B元素拖拽,A元素即为源对象,B元素即为目标对象。
不同的对象产生不同的拖放事件。
触发对象 | 事件名称 | 说明 |
---|---|---|
源对象 | dragstart | 源对象开始被拖动时触发 |
drag | 源对象被拖动过程中反复触发 | |
dragend | 源对象拖动结束时触发 | |
目标对象 | dragenter | 源对象开始进入目标对象范围内触发 |
dragover | 源对象在目标对象范围内移动时触发 | |
dragleave | 源对象离开目标对象范围时触发 | |
drop | 源对象在目标对象范围内被释放时触发 |
需要注意的是:
dragenter
和dragover
事件的默认行为是拒绝接受任何被拖放的元素。因此,我们要在这两个拖放事件中使用preventDefault
来阻止浏览器的默认行为;而且目标对象想要变成可释放区域,必须设置dragover
和drop
事件处理程序属性。
拖放还有其他一些属性和方法,但对于我们今天要实现的效果,只需要掌握上面提到的这些属性和方法就可以了,如果你想深入探究,可以参考HTML拖放API
拖拽排序
下面我们利用拖放API来实现列表的拖拽排序,代码基于Vue实现。
先不考虑排序动画。代码并不复杂,就全部贴了出来,然后解释一下实现思路:
- 由于拖动是实时的,所以没有使用
drop
而是使用了dragenter
触发排序。 - 在源对象开始被拖拽时记录其索引
dragIndex
,当它进入目标对象时(对应dragenter
事件),将其插入到目标对象的位置。 - 其中
dragenter
方法中有一个判断this.dragIndex !== index
(index为当前目标对象的索引),这是因为源对象同时也是目标对象,当没有这个判断时,源对象开始被拖拽时就会立刻触发自身的dragenter
事件,这是不合理的。
最终效果如下图所示:
<template>
<ul class="list"> <li @dragenter="dragenter($event, index)" @dragover="dragover($event, index)" @dragstart="dragstart(index)" draggable v-for="(item, index) in list" :key="item.label" class="list-item" > {{item.label}} </li> </ul>
</template>
<script> export default { data() { return { list: [ { label: '列表1' }, { label: '列表2' }, { label: '列表3' }, { label: '列表4' }, { label: '列表5' }, { label: '列表6' }, ], dragIndex: '', enterIndex: '', }; }, methods: { dragstart(index) { this.dragIndex = index; }, dragenter(e, index) { e.preventDefault(); // 避免源对象触发自身的dragenter事件 if (this.dragIndex !== index) { const source = this.list[this.dragIndex]; this.list.splice(this.dragIndex, 1); this.list.splice(index, 0, source); // 排序变化后目标对象的索引变成源对象的索引 this.dragIndex = index; } }, dragover(e, index) { e.preventDefault(); }, }, }; </script>
<style lang="scss" scoped> .list { list-style: none; .list-item { cursor: move; width: 300px; background: #EA6E59; border-radius: 4px; color: #FFF; margin-bottom: 6px; height: 50px; line-height: 50px; text-align: center; } } </style>
减去HTML和CSS,核心的dragenter
只有不超过15行的代码,就可实现一个拖拽排序功能。当然这个拖拽效果有点生硬,下面使用Vue自带的transition-group
组件,给排序添加平滑的过渡效果。
排序动画
如果不熟悉Vue的transition-group
,请先学习Vue的列表的排序过渡,这里不再赘述。
为了便于和上面的代码进行比较,同样一次性把全部代码贴出,可以看到代码变动并不大,只需把HTML的ul
元素改为transition-group
,在methods
中新增shuffle
方法,在CSS中新增一个过渡transition: transform .3s;
即可实现开头第一张图所展示的拖拽排序效果了。
你可以点击此链接进行在线体验。
<template>
<div> <transition-group name="drag" class="list" tag="ul" > <li @dragenter="dragenter($event, index)" @dragover="dragover($event, index)" @dragstart="dragstart(index)" draggable v-for="(item, index) in list" :key="item.label" class="list-item"> {{item.label}} </li> </transition-group> </div>
</template>
<script> export default { data() { return { list: [ { label: '列表1' }, { label: '列表2' }, { label: '列表3' }, { label: '列表4' }, { label: '列表5' }, { label: '列表6' }, ], dragIndex: '', enterIndex: '', }; }, methods: { shuffle() { this.list = this.$shuffle(this.list); }, dragstart(index) { this.dragIndex = index; }, dragenter(e, index) { e.preventDefault(); // 避免源对象触发自身的dragenter事件 if (this.dragIndex !== index) { const moving = this.list[this.dragIndex]; this.list.splice(this.dragIndex, 1); this.list.splice(index, 0, moving); // 排序变化后目标对象的索引变成源对象的索引 this.dragIndex = index; } }, dragover(e, index) { e.preventDefault(); }, }, }; </script>
<style lang="scss" scoped> .list { list-style: none; .drag-move { transition: transform .3s; } .list-item { cursor: move; width: 300px; background: #EA6E59; border-radius: 4px; color: #FFF; margin-bottom: 6px; height: 50px; line-height: 50px; text-align: center; } } </style>
如果想使用原生JS实现列表的拖拽排序和过渡动画,可以参考这篇文章:js drag拖动排序
今天的文章基于Vue快速实现列表拖拽排序分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/14640.html