一个简单的拖拽功能

254 阅读4分钟

前言

本文主要讲述基于 HTML5 的拖放(Drag and Drop)API 实现的简单拖拽功能,代码和思路如下

废话少说 直接上效果 拖拽元素.gif

Let's show you the code

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>拖拽demo</title>
		<style>
			body {
				display: flex;
			}
			.box1,
			.box2 {
				width: 300px;
				height: 600px;
				border: 1px solid rgb(250, 10, 10);
			}
			.box2 {
				margin-left: 50px;
			}

			.item {
				width: 100px;
				height: 50px;
				display: flex;
        border-radius: 50%;
				justify-content: center;
				align-items: center;
				background-color: rgb(135, 235, 140);
				margin-top: 10px;
				margin-left: 10px;
			}
		</style>
	</head>
	<body>
		<div class="box1">
			<div class="item" draggable="true" ondragstart="dragStart(event,'阿里')">
				阿里
			</div>
			<div class="item" draggable="true" ondragstart="dragStart(event,'腾讯')">
				腾讯
			</div>
			<div class="item" draggable="true" ondragstart="dragStart(event,'字节')">
				字节
			</div>
      <div class="item" draggable="true" ondragstart="dragStart(event,'滴滴')">
				滴滴
			</div>
		</div>
		<div
			class="box2"
			ondrop="dropEnd(event)"
		></div>
	</body>

	<script>
		const box2 = document.querySelector('.box2')
		function dragStart(e, device) {
			e.dataTransfer.setData('device', device)
		}
		function dropEnd(e) {
			var transferredDevice = e.dataTransfer.getData('device')
			const div = document.createElement('div')
			div.textContent = transferredDevice
			div.classList.add('item')
			box2.appendChild(div)
		}
    // 添加拖拽进入和离开的视觉反馈
    box2.addEventListener('dragover', function(e) {
            e.preventDefault(); // 允许放置
            box2.classList.add('over');
        });

    box2.addEventListener('dragleave', function() {
        box2.classList.remove('over');
    });
	</script>
</html>

实现思路

  1. 设置可拖拽元素:

    • 使用 draggable="true" 属性来标记一个元素是可拖拽的。通过给每个 div.item 元素添加 draggable="true" 实现的。
  2. 开始拖拽(dragstart 事件) :

    • 当用户开始拖动一个元素时,会触发 dragstart 事件。
    • dragstart 事件的处理函数中,可以使用 event.dataTransfer.setData() 方法来设置需要传递的数据。这里的数据是被拖动元素的标识或其他相关信息。
  3. 允许放置(dragover 事件) :

    • 默认情况下,无法将元素放置到其他元素上。为了允许放置,需要阻止 dragover 事件的默认行为。
    • dragover 事件的处理函数中调用 event.preventDefault() 方法,可以允许元素被放置。
  4. 处理放置(drop 事件) :

    • 当拖拽的元素被放置到目标区域时,会触发 drop 事件。
    • drop 事件的处理函数中,可以使用 event.dataTransfer.getData() 方法来获取之前设置的数据。
    • 然后,可以根据获取的数据执行相关操作,如在目标区域创建新元素或移动原始元素。
  5. 结束拖拽(dragend 事件) :

    • 当拖拽操作完成(无论是成功放置还是取消)时,会触发 dragend 事件。
    • 可以在 dragend 事件的处理函数中执行一些清理工作。

接下来尝试下左右互相拖拽的感觉

直接看效果

拖拽元素1.gif

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>拖拽demo</title>
    <style>
        body {
            display: flex;
        }
        .box1, .box2 {
            width: 300px;
            height: 600px;
            border: 1px solid rgb(250, 10, 10);
            padding: 10px;
        }
        .box2 {
            margin-left: 50px;
        }
        .item {
            width: 100px;
            height: 50px;
            display: flex;
            border-radius: 50%;
            justify-content: center;
            align-items: center;
            background-color: rgb(135, 235, 140);
            margin-bottom: 10px;
            cursor: pointer; /* 添加手指指针样式 */
        }
        .over {
            border: 2px dashed #000; /* 当拖拽元素进入时,显示虚线边框 */
        }
    </style>
</head>
<body>
    <div class="box1" ondrop="drop(event)" ondragover="allowDrop(event)">
        <div class="item" draggable="true" id="item1">阿里</div>
        <div class="item" draggable="true" id="item2">腾讯</div>
        <div class="item" draggable="true" id="item3">字节</div>
        <div class="item" draggable="true" id="item4">滴滴</div>
    </div>
    <div class="box2" ondrop="drop(event)" ondragover="allowDrop(event)"></div>

    <script>
        function dragStart(e) {
            e.dataTransfer.setData('text/plain', e.target.id);
        }

        function drop(e) {
            e.preventDefault();
            var data = e.dataTransfer.getData('text/plain');
            var draggedElement = document.getElementById(data);
            e.target.appendChild(draggedElement);
        }

        function allowDrop(e) {
            e.preventDefault();
            if (e.target.className === "box1" || e.target.className === "box2") {
                e.target.classList.add('over');
            }
        }

        // 给所有可拖拽的元素添加事件监听
        document.querySelectorAll('.item').forEach(item => {
            item.addEventListener('dragstart', dragStart);
        });

        // 给 box1 和 box2 添加 dragleave 事件监听
        document.querySelectorAll('.box1, .box2').forEach(box => {
            box.addEventListener('dragleave', function(e) {
                if (e.target.className === "box1" || e.target.className === "box2") {
                    e.target.classList.remove('over');
                }
            });
        });
    </script>
</body>
</html>

具体思路

  1. 设置元素为可拖拽
  • 为每个需要拖拽的元素设置 draggable="true" 属性。
  • 为这些元素添加一个唯一的 id,以便在拖拽过程中识别它们。
  1. 捕获拖拽开始的事件
  • 为所有可拖拽的元素添加 dragstart 事件监听器。
  • dragstart 事件的处理函数中,使用 event.dataTransfer.setData() 方法存储被拖拽元素的 id
  1. 允许在目标容器上放置
  • 为目标容器(在这个案例中是 box1box2)添加 dragover 事件监听器。
  • dragover 事件的处理函数中,调用 event.preventDefault() 方法来阻止默认行为,从而允许在这些容器上放置元素。
  1. 处理元素放置
  • 为目标容器添加 drop 事件监听器。
  • drop 事件的处理函数中,使用 event.dataTransfer.getData() 方法获取被拖拽元素的 id
  • 使用 document.getElementById() 获取被拖拽的元素,并将其追加到触发 drop 事件的容器中。由于 appendChild 方法实际上是移动元素,所以被拖拽的元素会从原来的位置移动到新位置,从而实现了删除原来元素的效果。
  1. 添加视觉反馈
  • dragoverdragleave 事件中,可以通过添加或移除 CSS 类来为用户提供视觉反馈,比如改变容器的边框样式。