项目地址
项目地址👉👉点击进入,可以直接设置为浏览器主页或者桌面快捷方式进行使用,本人在用,长期维护。
源码地址
完全开源,大家可以随意研究,二次开发。当然还是十分欢迎大家点个Star⭐⭐⭐
👉👉源码链接(gitee) 👉👉源码链接(github)
需求介绍
上面是我一个开源的用来收藏网站的小项目,但是现阶段只能一个一个的按顺序添加网址,这样就产生了一个问题,那就是后添加的一定在下面,而我如果新添加了一个比较常用的网站,而列表又过长的话,每次进入都需要翻到下面去找,实在是太不方便。
所以我就想添加一个拖拽排序的功能,在编辑模式下,可以通过拖拽图标进行排序,退出编辑模式自动保存,这样就解决了上面的问题,优化了用户体验。
下面就详细记录一下此功能的实现。
HTML拖放接口
首先还是先学习一下API
官方介绍
HTML 拖放(Drag and Drop)接口使应用程序能够在浏览器中使用拖放功能。例如,用户可使用鼠标选择可拖拽(draggable)元素,将元素拖拽到可放置(droppable)元素,并释放鼠标按钮以放置这些元素。拖拽操作期间,会有一个可拖拽元素的半透明快照跟随着鼠标指针。
拖拽事件
可用的拖拽事件一共有七个,其中三个是用于拖拽元素的
-
dragstart 在元素开始被拖动时触发
-
dragend 在拖动操作完成时触发
-
drag 在元素被拖动时触发 四个是用于释放区域的
-
dragenter 当被拖动元素进入到释放区所占据的屏幕空间时触发
-
dragover 当被拖动元素在释放区内移动时触发
-
dragleave 当被拖动元素没有放下就离开释放区时触发
-
drop 当被拖动元素在释放区里放下时触发
步骤
- 首先给被拖放的元素添加
draggable属性并添加dragstart事件处理函数 - 定义拖放数据,本例定义的是拖拽元素的
id - 定义一个释放区域,添加
drop和dragover事件处理函数,并阻止默认事件 - 处理拖放数据
- 拖放结束,添加
dragend事件处理函数
例子
代码
<body>
<div class="box" id='box1'>
<div id="child" draggable="true"></div>
</div>
<div class="box" id='box2'>
</div>
</body>
<script>
((doc) => {
const box1 = doc.querySelector("#box1")
const box2 = doc.querySelector("#box2")
const child = doc.querySelector("#child")
// 绑定事件函数
const bindEvents = () => {
child.addEventListener("drag", handleDrag)
child.addEventListener("dragstart", handleDragstart)
child.addEventListener("dragend", handleDragend)
box2.addEventListener("dragenter", handleDragenter)
box2.addEventListener("dragover", handleDragover)
box2.addEventListener("dragleave", handleDragleave)
box2.addEventListener("drop", handleDrop)
};
// 在元素被拖动时触发
const handleDrag = (ev) => {
console.log('🚀🚀 ~ drag 触发啦');
}
// 在元素开始被拖动时触发
const handleDragstart = (ev) => {
console.log('🚀🚀 ~ dragstart 触发啦');
ev.dataTransfer.setData('id', ev.target.id);
}
// 在拖动操作完成时触发
const handleDragend = (ev) => {
console.log('🚀🚀 ~ dragend 触发啦');
}
// 当被拖动元素进入到释放区所占据的屏幕空间时触发
const handleDragenter = (ev) => {
console.log('🚀🚀 ~ dragenter 触发啦');
}
// 当被拖动元素在释放区内移动时触发
const handleDragover = (ev) => {
ev.preventDefault();
console.log('🚀🚀 ~ dragover 触发啦');
}
// 当被拖动元素没有放下就离开释放区时触发
const handleDragleave = (ev) => {
console.log('🚀🚀 ~ dragleave 触发啦');
}
// 当被拖动元素在释放区里放下时触发
const handleDrop = (ev) => {
console.log('🚀🚀 ~ drop 触发啦');
ev.preventDefault();
const data = ev.dataTransfer.getData("id");
ev.target.appendChild(document.getElementById(data));
}
// 初始化函数
const init = () => {
bindEvents();
};
// 执行初始化函数
init();
})(document);
</script>
在VUE3中的实现思路
原生js实现拖拽排序我还没有弄,但是在vue中就非常的简单,因为我们在触发任何事件的时候,都可以拿到元素的index,我们可以靠index轻易实现。
- 在
dragstart中记录下旧的索引 - 在
dragover中记录下新的索引,每次经过一个都会更新 - 在
drop事件中处理数组,删掉旧的元素,在目标索引添加新的元素
//简略后的伪代码 详情请查看源码
<div draggable="true" v-for='(item,index) in markList' :key='index'
@dragstart="handleDragstart(index)"
@drop.prevent="handleDrop()"
@dragover.prevent="handleDragover(index)">
</div>
//js
const state = reactive<MarkState>({
// 当前正在拖拽的元素索引
oldItemIndex: -1,
// 将插入的目标位置索引
newItemIndex: -1,
});
// 开始拖拽时触发
const handleDragstart = (index: number) => {
state.oldItemIndex = index;
};
// 停止拖拽时触发
const handleDrop = () => {
// 如果位置没有发生改变 什么也不做
if (state.newItemIndex === state.oldItemIndex) {
return;
}
// 如果位置发生了改变
emit('change-mark-index', state.oldItemIndex, state.newItemIndex);
};
// 拖拽经过其他元素时触发
const handleDragover = (index: number) => {
state.newItemIndex = index;
};
//change-mark-index
// 修改书签索引
const changeMarkIndex = (oldItemIndex: number, newItemIndex: number) => {
// 删除老的
const changeItem = marks.value.splice(oldItemIndex, 1)[0];
// 在列表中目标位置增加新的
marks.value.splice(newItemIndex, 0, changeItem);
};
最终实现
接下来的开发计划
💡拖拽到目标位置后会有位置提示
💡支持拖拽到其他标签下
💡添加拖拽动画
拖拽动画这里我试了vue内部的 transition-group 但是没有成功,如果大家有什么思路或者建议,请大家不吝赐教,在下方评论或者私信我,十分感谢🙏🙏🙏.
最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧
链接整合
🔊项目预览地址(GitHub Pages):👉👉alanhzw.github.io
🔊项目预览备用地址(自己的服务器):👉👉warbler.duwanyu.com
🔊源码地址(gitee):👉👉gitee.com/hzw_0174/wa…
🔊源码地址(github):👉👉github.com/alanhzw/War…
🔊我的博客:👉👉www.duwanyu.com