本文已参与「新人创作礼」活动,一起开启掘金创作之路。
最近开始准备面试,在做一些公司的测评时,看到页面上的一种拖拽选项框。为了加深对HTML5拖拽事件的理解,打算自己实现一个出来。
基本实现
内容大概是这样的,将左边的内容拖拽到右边,使其符合自己的个人情况。
啪的一下,很快啊,把静态页面写好了:
<!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>拖拽测试</title>
<style>
.box{
border: 1px solid black;
width: 700px;
height: 400px;
display: flex;
}
.left,.right{
width: 50%;
height: 100%;
}
.left-box{
width: 60%;
height: 20%;
text-align: center;
line-height: 500%;
margin: 40px auto;
border: 1px dotted ;
}
.left-box-content{
width: 100%;
height: 100%;
background-color: rgb(202, 235, 247);
}
.right-box{
width: 70%;
height: 30%;
margin: 0 auto;
border: 1px dotted black;
display: flex;
justify-content: center;
align-items: center;
}
</style>
</head>
<body>
<div class="box">
<div class="left">
<div class="left-box">
<div id="drag1" class="left-box-content">第一个特征</div>
</div>
<div class="left-box">
<div id="drag2" class="left-box-content">第二个特征</div>
</div>
<div class="left-box">
<div id="drag3" class="left-box-content">第三个特征</div>
</div>
</div>
<div class="right">
<h3>最符合的</h3>
<div class="right-box" id="drop1">
</div>
<h3>最不符合的</h3>
<div class="right-box" id="drop2"></div>
</div>
</div>
</body>
</html>
对于html拖拽事件,存在几个关键点:
被拖拽的元素是谁?如何让元素允许被拖拽?
在这里,拖拽元素是id为drag1、drag2、drag3,如果需要允许拖动,则将这三个元素设置draggable="true"。
如何获取被拖动的元素及内容?
通过ondragstart和event.dataTransfer.setData()来实现记录。
拖拽的元素可以被放置到何处?
在这里,元素drop1、drop2为可放置位置,通过ondragover事件来确定。
怎么处理放置后的逻辑?
往目的区域添加元素。通过ondrop事件进行处理。
明确了上面几条后,啪的一下又很快啊:
<body>
<div class="box">
<div class="left">
<div class="left-box">
<div id="drag1" ondragstart="drag(event)" draggable="true" class="left-box-content">第一个特征</div>
</div>
<div class="left-box">
<div id="drag2" ondragstart="drag(event)" draggable="true" class="left-box-content">第二个特征</div>
</div>
<div class="left-box">
<div id="drag3" ondragstart="drag(event)" draggable="true" class="left-box-content">第三个特征</div>
</div>
</div>
<div class="right">
<h3>最符合的</h3>
<div class="right-box" id="drop1" ondragover="allowDrop(event)" ondrop="drop(event)">
</div>
<h3>最不符合的</h3>
<div class="right-box" id="drop2" ondragover="allowDrop(event)" ondrop="drop(event)"></div>
</div>
</div>
</body>
<script>
function drag(event){
event.dataTransfer.setData("item",event.target.id); // 规定了被拖拽的是什么
}
function allowDrop(event){
event.preventDefault(); // 默认地,无法将数据/元素放置到其他元素中。因此需要取消默认行为。
}
// 放置
function drop(event){
event.preventDefault(); // ondrop事件的默认行为是以链接的形式打开,需要禁止。
let elId = event.dataTransfer.getData("item");
event.target.appendChild(document.getElementById(elId));
}
</script>
效果如下:
目前来说,到这里就基本实现功能了,但还有很多可以优化的地方,下面开始优化。
优化
首先,右边的一个选择框只能有一个元素:
个人思路是判断一下目标放置位置是否为指定位置即可。
// 放置
function drop(event){
event.preventDefault(); // ondrop事件的默认行为是以链接的形式打开,需要禁止。
// 如果拖拽的元素不是drop1或者drop2,那就取消拖拽
if(event.target.id == "drop1" || event.target.id == "drop2"){
let elId = event.dataTransfer.getData("item");
event.target.appendChild(document.getElementById(elId));
}
}
效果如下:
完美!
接着,点击已经拖拽的元素之后,将元素返回原来的位置。 个人思路:用一个对象存储所有可拖拽元素以及原来的位置。点击标签的时候,将元素
当然了需要原来放置标签的盒子设置id。给原来的标签外部的盒子添加id(box1、box2、box3)
<div class="left-box" id="box1">
<div id="drag1" onclick="reset(event)" ondragstart="drag(event)" draggable="true"
class="left-box-content">第一个特征</div>
</div>
<div class="left-box" id="box2">
<div id="drag2" onclick="reset(event)" ondragstart="drag(event)" draggable="true"
class="left-box-content">第二个特征</div>
</div>
<div class="left-box" id="box3">
<div id="drag3" onclick="reset(event)" ondragstart="drag(event)" draggable="true"
class="left-box-content">第三个特征</div>
</div>
相关逻辑:
// 还原元素位置
let origin = new Map();
origin.set("drag1", "box1");
origin.set("drag2", "box2");
origin.set("drag3", "box3");
function reset(event) {
const id = event.target.id;
const parentNode = event.target.parentNode;
// 判断是否要还原元素
if (parentNode.id !== origin.get(id)) {
console.log("该元素被拖拽了,需要还原");
document.getElementById(origin.get(id)).appendChild(event.target)
} else {
console.log("该元素在原位置");
}
}
效果如下:
先拖拽drag1然后依次点击drag2和drag1,控制台输出如下:
到这里就完成了基本功能了。
写这个小demo主要为了加深对拖拽事件的实现和理解。需要明确几个关键点:
- 拖拽的对象?如何允许拖拽?
- 获取拖拽对象内容?
- 拖拽目的地是什么?
- 如何处理拖拽逻辑?
源码
<!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>拖拽测试</title>
<style>
.box {
border: 1px solid black;
width: 700px;
height: 400px;
display: flex;
}
.left,
.right {
width: 50%;
height: 100%;
}
.left-box {
width: 60%;
height: 20%;
text-align: center;
line-height: 500%;
margin: 40px auto;
border: 1px dotted;
}
.left-box-content {
width: 100%;
height: 100%;
background-color: rgb(202, 235, 247);
}
.right-box {
width: 70%;
height: 30%;
margin: 0 auto;
border: 1px dotted black;
text-align: center;
line-height: 800%;
}
</style>
</head>
<body>
<div class="box">
<div class="left">
<div class="left-box" id="box1">
<div id="drag1" onclick="reset(event)" ondragstart="drag(event)" draggable="true"
class="left-box-content">第一个特征</div>
</div>
<div class="left-box" id="box2">
<div id="drag2" onclick="reset(event)" ondragstart="drag(event)" draggable="true"
class="left-box-content">第二个特征</div>
</div>
<div class="left-box" id="box3">
<div id="drag3" onclick="reset(event)" ondragstart="drag(event)" draggable="true"
class="left-box-content">第三个特征</div>
</div>
</div>
<div class="right">
<h3>最符合的</h3>
<div class="right-box" id="drop1" ondragover="allowDrop(event)" ondrop="drop(event)">
</div>
<h3>最不符合的</h3>
<div class="right-box" id="drop2" ondragover="allowDrop(event)" ondrop="drop(event)"></div>
</div>
</div>
</body>
<script>
function drag(event) {
event.dataTransfer.setData("item", event.target.id); // 规定了被拖拽的是什么?
}
function allowDrop(event) {
event.preventDefault(); // 默认地,无法将数据/元素放置到其他元素中。因此需要取消默认行为。
}
// 放置
function drop(event) {
event.preventDefault(); // ondrop事件的默认行为是以链接的形式打开,需要禁止。
// 如果拖拽的元素不是drop1或者drop2,那就取消拖拽
if (event.target.id == "drop1" || event.target.id == "drop2") {
let elId = event.dataTransfer.getData("item");
event.target.appendChild(document.getElementById(elId));
}
}
// 还原元素位置
let origin = new Map();
origin.set("drag1", "box1");
origin.set("drag2", "box2");
origin.set("drag3", "box3");
function reset(event) {
const id = event.target.id;
const parentNode = event.target.parentNode;
// 判断是否要还原元素
if (parentNode.id !== origin.get(id)) {
console.log("该元素被拖拽了,需要还原");
document.getElementById(origin.get(id)).appendChild(event.target)
} else {
console.log("该元素在原位置");
}
}
</script>
</html>