前言
最近这些日子在做低代码相关的东西,拖放效果又是其中的关键交互,之前一直用得包装好的三方库,没有对拖放的 API 有过深入的了解,今天写一篇文章来聊聊其中的点,如果有任何的错误,欢迎大家指正。
了解拖放 API
首先打开mdn看看拖放相关的 API,相关的一共有七个事件:
| drag | dragstart | dragend | dragleave | dragenter | dragover |
|---|---|---|---|---|---|
| 当拖拽元素或选中的文本时触发。 | 当用户开始拖拽一个元素或选中的文本时触发 | 当拖拽操作结束时触发 (比如松开鼠标按键或敲“Esc”键). | 当拖拽元素或选中的文本离开一个可释放目标时触发。 | 当拖拽元素或选中的文本到一个可释放目标时触发 | 当元素或选中的文本被拖到一个可释放目标上时触发(每 100 毫秒触发一次)。 |
光看文字也不知道每个事件啥时候触发,先写个 demo 来看看每个事件的触发时机。
拖放demo
<div class="dustbin">我是触发区域</div>
<div class="dragbox">
<div class="draglist" title="拖拽我" draggable="true">列表1</div>
<div class="draglist" title="拖拽我" draggable="true">列表2</div>
<div class="draglist" title="拖拽我" draggable="true">列表3</div>
<div class="draglist" title="拖拽我" draggable="true">列表4</div>
<div class="draglist" title="拖拽我" draggable="true">列表5</div>
<div class="draglist" title="拖拽我" draggable="true">列表6</div>
</div>
<style>
.dustbin {
background-color: skyblue;
padding: 20px;
}
</style>
<script>
for (const ele of $(".draglist")) {
ele.addEventListener("dragstart", (event) => {
console.log("drag-start");
});
ele.addEventListener("dragend", (event) => {
console.log("drag-end");
});
}
$(".dustbin")[0].addEventListener("dragover", (event) => {
console.log("drag-over");
event.preventDefault();
});
$(".dustbin")[0].addEventListener("dragenter", function (event) {
console.log("drag-enter");
event.preventDefault();
this.style.color = "#ffffff";
});
$(".dustbin")[0].addEventListener("drop", function (event) {
console.log("drop");
});
</script>
- 在点击并拖动“列表 6”的时候,触发了可拖动元素的
dragstart事件 - 在拖动“列表 6”元素时“我是触发区域”元素时,触发了可拖动区域元素
dragenter事件 - 在拖动元素停留在可触发区域元素时,每 100ms 触发一次可拖动区域元素
dragover事件 - 移出可触发区域元素时,触发可拖动区域元素
drag-leave事件 - 在可触发区域放下拖动元素时,会触发可拖动区域元素的
drop事件 - 在可触发区域放下拖动元素时,会触发可拖动元素的
drag-end事件
这里有几个需要关注的点: 在 HTML 中,除了图像、链接和选择的默认行为外,默认情况下没有其他元素可拖动。 要使其他 HTML 元素可拖动,必须完成三件事:
draggable将属性设置为"true"您希望使其可拖动的元素。- 为
dragstart事件添加一个监听器。 - 在上面的监听器中设置拖动数据。
支持拖放并修改dom
这个demo会在拖动元素至“垃圾箱区域”时,会将这个元素从dom中移除,只是增加了一点点待代码。
<div class="dustbin">垃圾箱</div>
<div class="dragbox">
<div class="draglist" title="拖拽我" draggable="true">列表1</div>
<div class="draglist" title="拖拽我" draggable="true">列表2</div>
<div class="draglist" title="拖拽我" draggable="true">列表3</div>
<div class="draglist" title="拖拽我" draggable="true">列表4</div>
<div class="draglist" title="拖拽我" draggable="true">列表5</div>
<div class="draglist" title="拖拽我" draggable="true">列表6</div>
</div>
<div class="dragremind"></div>
<style>
.dustbin {
background-color: skyblue;
padding: 20px;
}
</style>
<script>
// 整个全局变量,保存在拖动中的dom元素
let eleDrag = null;
for (const ele of $(".draglist")) {
ele.addEventListener("dragstart", (event) => {
console.log("drag-start");
event.dataTransfer.dropEffect = "move";
event.dataTransfer.effectAllowed = "move";
// 拖动开始时,保存dom元素
eleDrag = event.target;
});
ele.addEventListener("dragend", (event) => {
console.log("drag-end");
// 在拖动结束时,移除dom元素
eleDrag = null;
});
}
$(".dustbin")[0].addEventListener("dragover", (event) => {
console.log("drag-over");
event.preventDefault();
});
$(".dustbin")[0].addEventListener("dragenter", function (event) {
console.log("drag-enter");
event.preventDefault();
this.style.color = "#ffffff";
});
$(".dustbin")[0].addEventListener("drop", function (event) {
console.log("drop");
// 在放下元素时,从dom中移除拖动的元素
if (eleDrag) {
eleDrag.parentNode.removeChild(eleDrag);
}
});
</script>
支持随意拖放
这个demo又稍微复杂了亿点点,添加了几行代码实现随意拖放。
其实就是在拖放结束时修改了拖放元素的top以及left值,看一下dragstart以及drop函数既就可以理解了。
<div class="container">
<div class="dragbox">
<div
class="draglist"
title="拖拽我"
draggable="true"
style="top: 0; left: 0"
>
列表1
</div>
<div
class="draglist"
title="拖拽我"
draggable="true"
style="top: 50px; left: 50px"
>
列表2
</div>
<div
class="draglist"
title="拖拽我"
draggable="true"
style="top: 100px; left: 150px"
>
列表3
</div>
<div
class="draglist"
title="拖拽我"
draggable="true"
style="top: 90px; left: 190px"
>
列表4
</div>
<div
class="draglist"
title="拖拽我"
draggable="true"
style="top: 10px; left: 90px"
>
列表5
</div>
<div
class="draglist"
title="拖拽我"
draggable="true"
style="top: 70px; left: 167px"
>
列表6
</div>
</div>
</div>
<style>
.container {
width: 1000px;
height: 1000px;
border: 1px solid gray;
overflow: hidden;
}
.dragbox {
position: relative;
}
.draglist {
position: absolute;
display: inline-block;
width: 40px;
height: 40px;
border: 1px solid gray;
}
</style>
<script>
let targetEle = null;
var store = {};
for (const ele of $(".draglist")) {
ele.addEventListener("dragstart", (event) => {
event.dataTransfer.dropEffect = "move";
event.dataTransfer.effectAllowed = "move";
store.x = event.pageX;
store.y = event.pageY;
store.top = parseFloat(getComputedStyle(event.target).top) || 0;
store.left = parseFloat(getComputedStyle(event.target).left) || 0;
targetEle = event.target;
});
ele.addEventListener("dragend", (event) => {
targetEle = null;
});
}
$(".container")[0].addEventListener("dragover", (event) => {
event.preventDefault();
});
$(".container")[0].addEventListener("dragenter", function (event) {
event.preventDefault();
});
$(".container")[0].addEventListener("drop", function (event) {
if (targetEle) {
var containerReact = targetEle.getBoundingClientRect();
var distanceX = event.pageX - store.x;
var distanceY = event.pageY - store.y;
var top = store.top + distanceY;
var left = store.left + distanceX;
targetEle.style.top = top + "px";
targetEle.style.left = left + "px";
}
});
</script>