前言
一个典型的drag操作是这样开始的:用户用鼠标选中一个可拖动的(draggable)元素,移动鼠标到一个可放置的(droppable)元素,然后释放鼠标。 在操作期间,会触发一些事件类型,有一些事件类型可能会被多次触发
第一部分 在拖动元素上
要使其他的 HTML 元素可拖动,必须做三件事:
- 在你想要拖动的元素上,将
draggable
属性设置成true
。 - 为事件添加一个监听器
dragstart
。 - 在上面定义的监听器中设置拖动数据
1.1 draggable: 可拖动属性
- draggable: 全局属性, 是一个枚举类型, 而不是布尔类型. 这意味着必须显式指定值为 true 或者 false ,像 <label draggable>Example Label</label> 这样的简写是不允许的。正确的用法是` <label draggable="true">Example Label</label>
- 默认情况下,只有已选中的文本、图片、链接可以拖动。对其它的元素来说,必须按拖动机制的顺序设置 ondragstart 事件才能正常工作
- 属性
draggable
设置为 true,所以这个元素变成可拖动的。如果该属性被省略或被设置为 false,则该元素将不会被拖动,而是只选中文本。- 注意,当一个元素被设置成可拖动的时候, 文本或者其中的其他元素不能再以正常的方式(通过鼠标点击和拖动)被选择。用户必须按住 alt 键,用鼠标选择文本,或者使用键盘来代替。
<!--
拖动元素, 设置全局拖动属性
-->
<div class="drop-item" draggable="true"></div>
1.2 ondragstart: 开始拖动事件
- 当用户开始拖动时,会触发 dragstart 事件(此事件会冒泡),
- 所有拖拽事件都有一个名为 dataTransfer的属性,它持有拖曳数据(dataTransfer 是一个 DataTransfer 对象)。
- 一个web应用需要添加拖拽功能时,应当仅仅使用DragEvent(事件对象)和DataTransfer(事件对象中的属性)接口即
DataTransfer对象
这个对象可以从所有拖动事件
drag events
的
dataTransfer
属性上获取,但是不能单独创建。
详情见MDN资料
-
属性:
- dropEffect: 影响到拖动过程中浏览器显示的鼠标样式
-
方法:
- setData: 设置拖动数据
/** * @name: 设置拖动元素的数据 * @param {type: 数据MIME} * @param {value: 数据值} * @return: none */ event.dataTransfer.setData(type, value); // =============== 示例 ============== /* * type类型: application/x-bookmark: 自定义类型 text/uri-list: URL text/plain: 文本 * 您可以以多种格式提供数据, 每种格式可以提供一次, 如果您试图以相同的格式添加两次数据,那么新的数据将替换旧的数据。 * 不能设置复杂类型数据, 此时可以通过反序列化解决 */ var dt = event.dataTransfer; dt.setData("application/x-bookmark", bookmarkString); dt.setData("text/uri-list", "http://www.mozilla.org"); dt.setData("text/plain", "http://www.mozilla.org");
- getData(): 获取设置的数据
/** * @name: 获取通过setData()设置的数据 * @param {type: 数据MIME} * @return: none */ event.dataTransfer.clearData(type); /* * 参数必填, 没有参数会直接报错 * 当参数的 MIME 没有对应值时, 返回空字符串 */ event.dataTransfer.getData("text/plain");
- clearData: 清除数据**
/** * @name: 清除拖动元素的数据 * @param {type?: 数据MIME} * @return: none */ event.dataTransfer.clearData(type); // ================ 示例 ================== /* * 如果没有参数调用此方法,或者格式为空 ,则将删除所有类型的数据。 * 该方法只能在dragstart 事件的处理程序中使用,因为这是拖动操作的数据存储只能写入的时间。 * */ event.dataTransfer.clearData("text/uri-list");
- setDragImage(): 设置拖动反馈图像
当一个拖动发生时,一个半透明的图像是由拖拽目标生成的("dragstart" 事件被触发的元素),并在拖动过程中跟踪鼠标指针。这个映像是自动创建的,所以你不需要自己创建它。但是,您仍然可以使用 setDragImage() 方法来自定义拖动反馈图像。
/** * @name: 设置拖动反馈图像 * @param {image: 图像的引用, 通常是图像元素, 也可以是画布(canvas) 或任何其他元素} * @param {xOffset: 相对于图片的横向偏移量} * @param {yOffset: 相对于图片的纵向偏移量} * @return: none */ event.dataTransfer.setDragImage(image, xOffset, yOffset); // ============== 示例 =========== /* * 引用通常是一个图像元素,但它也可以是画布(canvas)或任何其他元素。 */ var canvas = document.createElementNS("http://www.w3.org/1999/xhtml","canvas"); canvas.width = canvas.height = 50; var ctx = canvas.getContext("2d"); ctx.lineWidth = 4; ctx.moveTo(0, 0); ctx.lineTo(50, 50); ctx.moveTo(0, 50); ctx.lineTo(50, 0); ctx.stroke(); var dt = event.dataTransfer; dt.setData('text/plain', 'Data to Drag'); dt.setDragImage(canvas, 25, 25);
第二部分 放置目标
web页面或应用程序的大多数区域都不是 drop 数据的有效位置。因此,这些事件的默认处理是不允许出现 drop。
如果您想要允许 drop,您必须通过取消事件来防止默认的处理, 在 dragover 事件中调用 preventDefault() 方法将表明在该位置允许 drop 。
1. dragover: 当元素或选中的文本被拖到一个可释放目标上时触发(每100毫秒触发一次)。
在 dragover 事件中调用 preventDefault() 方法, 但是这样的话, 会将所有拖动元素放置进来, 通常希望只在某些情况下调用 preventDefault() 方法, 这时可以通过检查拖放元素的 dataTransfer 的types属性(而使用ev.dataTransfer.getData("text/plain") 这种方式无用, 因为在dragover事件中无法获取到数据的)
-
types属性: 拖动操作中使用的数据格式数组。每种格式都是字符串类型。如果拖动操作不包含数据,则此数组列表将为空。如果拖动操作中包含任何文件,则其中一个类型将是Files。
特别注意: 这是一个只读属性, 如果通过enent.dataTransfer.types直接查看数据
// 可以通过判断 types 中存放的数据类型来判断是否是需要的拖动元素 (因为图片, 链接也是默认可以拖动的) -- form/item(可以自定义类型的) if ([...e.dataTransfer.types].includes("form/item")) { e.preventDefault(); }
2. drop: 拖动元素放置的时候触发
处理放置效果, 在drop事件中处理, 在这个事件中可以调用event.dataTransfer.getData()来获取数据
注意每个处理程序调用 preventDefault() 来阻止对这个事件的其它处理过程(如触点事件或指针事件)
// 拖动第四步: 在拖动元素放置到 放置目标 , 执行一系列的操作
// drop事件: 当元素或选中的文本在可释放目标上被释放时触发
box.addEventListener("drop", function(e) {
console.log(e.dataTransfer.getData("form/item"));
e.preventDefault();
this.innerText = e.dataTransfer.getData("form/item");
});
第三部分 简单实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<style>
* {
margin: 0;
bottom: 0;
}
div {
display: inline-block;
margin-right: 100px;
}
.dorp {
width: 100px;
height: 100px;
background: #000;
}
.box {
width: 300px;
height: 300px;
border: 1px solid red;
}
.box2 {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<!-- 拖动第一步: 设置可拖动属性 -->
<div class="dorp" draggable="true"></div>
<div class="box">
<div class="box2"></div>
</div>
<div class="dorp" draggable="true"></div>
</body>
</html>
<script>
let dorp = document.querySelector(".dorp");
let box = document.querySelector(".box");
// 拖动第二步: 开始拖动时, 设置拖动数据
// dragstart事件: 当用户开始拖动一个元素或选中的文本时触发
dorp.addEventListener("dragstart", e => {
// 获取dateTransfer接口
let dt = e.dataTransfer;
// 设置拖动过程中数据 -- 可以自定义的
dt.setData("form/item", "这可能是一个下拉框选项");
});
// 拖动第三步: 在放置目标上处理dragover事件 ==> 阻止默认事件(可以根据数据类型不同来判断是否可以放置)
// dragover事件: 当元素或选中的文本被拖到一个可释放目标上时触发(每100毫秒触发一次)。
box.addEventListener("dragover", e => {
// 可以通过判断 types 中存放的数据类型来判断是否是需要的拖动元素 (因为图片, 链接也是默认可以拖动的)
if ([...e.dataTransfer.types].includes("form/item")) {
e.preventDefault();
}
});
// 拖动第四步: 在拖动元素放置到 放置目标 , 执行一系列的操作
// drop事件: 当元素或选中的文本在可释放目标上被释放时触发
box.addEventListener("drop", function(e) {
console.log(e.dataTransfer.getData("form/item"));
e.preventDefault();
this.innerText = e.dataTransfer.getData("form/item");
});
</script>
最后
只是简单学习了一下H5的拖放过程, 没有深入的细究, 还有好多事件 和 属性在一定的场景下有作用的