拖拽事件
1. 拖拽事件类型
事件名 | 触发时机 | 绑定对象 |
---|---|---|
dragstart | 用户开始拖动 HTML 元素或选中的文本 | 拖拽元素 |
drag | 正在拖动元素或文本选区(在此过程中持续触发,每 350ms 触发一次) | 拖拽元素 |
dragend | 拖放操作结束(松开鼠标按钮或按下 Esc 键) | 拖拽元素 |
dragenter | 被拖动的元素或文本选区移入有效释放目标区 | 目标元素 |
dragover | 被拖动的元素或文本选区正在有效释放目标上被拖放(在此过程中持续触发,每 350ms 触发一次) | 目标元素 |
dragleave | 被拖动的元素或文本选区移出有效释放目标区 | 目标元素 |
drop | 元素在有效释放目标区上释放 | 目标元素 |
2. 拖拽条件
- 被拖拽元素
draggble
属性为true
<!-- p标签可以被拖动 -->
<p draggble = "true">一个div</p>
- 目标元素可被接收被拖拽元素释放=====> 一般在
dragover
事件中阻止事件默认行为
<div class="container">给我一个div</div>
<script>
onMounted(()=> {
const container = document.querySelector('.container')
// 被拖拽元素到目标元素内时 在dragover事件中阻止默认行为
container.addEventListener('dragover', (e) => {
e.preventDefault()
})
})
</script>
示例:
注意:
当从操作系统向浏览器中拖拽文件时,不会触发 dragstart 和dragend 事件。
3.dataTransfer对象
DataTransfer 对象用于保存拖动并放下(drag and drop)过程中的数据。它可以保存一项或多项数据,这些数据项可以是一种或者多种数据类型。
4.dataTransfer对象属性
dataTransfer.dropEffect
DataTransfer.dropEffect属性用来设置放下(drop)被拖拉节点时的效果(鼠标样式),会影响到拖拉经过相关区域时鼠标的形状。它可能取下面的值:
copy:复制被拖拉的节点
move:移动被拖拉的节点
link:创建指向被拖拉的节点的链接
none:无法放下被拖拉的节点
typescripttarget.addEventListener('dragover', function (e) { e.preventDefault(); e.dataTransfer.dropEffect = 'copy'; });
除了上面这些值,设置其他的值都是无效的。
dropEffect
属性一般在 dragenter 和 dragover 事件的监听函数中设置,对于dragstart、drag、dragleave这三个事件,该属性不起作用。因为该属性只对接受被拖拉的节点的区域有效,对被拖拉的节点本身是无效的。进入目标区域后,拖拉行为会初始化成设定的效果
dataTransfer.effectAllowed
DataTransfer.effectAllowed 属性指定拖放操作所允许的一个效果。copy 操作用于指示被拖动的数据将从当前位置复制到放置位置。move 操作用于指定被拖动的数据将被移动。link 操作用于指示将在源和放置位置之间创建某种形式的关系或连接。
应该在
dragstart
事件中设置此属性,以便为拖动源设置所需的拖动效果。在 dragenter 和dragover 事件处理程序中,该属性将设置为在dragstart 事件期间分配的任何值,因此,可以使用effectAllowed来确定允许哪个效果
- copy:复制被拖拉的节点
- move:移动被拖拉的节点
- link:创建指向被拖拉节点的链接
- copyLink:允许copy或link
- copyMove:允许copy或move
- linkMove:允许link或move
- all:允许所有效果
- none:无法放下被拖拉的节点
- uninitialized:默认值,等同于all
source.addEventListener('dragstart', function (e) {
e.dataTransfer.effectAllowed = 'move';
});
target.addEventListener('dragover', function (e) {
e.dataTransfer.dropEffect = 'move';
});
关于effectAllowed 和 dropEffect:
effectAllowed
: 相当于总规则 允许使用哪些效果dropEffect
为具体的效果 dropEffect的值 必须被包含 在effectAllowed设置的值内只要dropEffect属性和effectAllowed属性之中,有一个为none,就无法在目标节点上完成 drop 操作
感觉没什么用 不知道是自己没了解真正的用途还是怎么 拿到就是为了设置个鼠标样式么
dataTransfer.files
DataTransfer.files属性是一个 FileList 对象,包含一组本地文件,可以用来在拖拉操作中传送。如果本次拖拉不涉及文件,则该属性为空的 FileList 对象。
示例:
<script setup lang='ts'>
import { onMounted, ref } from 'vue'
const fileList = ref<File[]>()
onMounted(() => {
const wraper = document.querySelector('.dragWraper')
// 在dragover事件中阻止事件默认行为 才能释放被拖拽元素
wraper?.addEventListener('dragover', (e) => {
e.preventDefault()
})
wraper?.addEventListener('drop', (e) => {
e.preventDefault()
// @ts-expect-error
fileList.value = e.dataTransfer.files
console.log('files', fileList.value)
})
})
</script>
<template>
<div
class="dragWraper m-auto mt-20px h200px w500px flex-col cursor-pointer bg-[#eee] f-c-c"
>
<div class="text-50px text-blue-500">
+
</div>
<div class="text-blue">
拖拽一个文件来试试
</div>
<div v-for="file in fileList" :key="file.name" class="text-[#f00]">
{{ file.name }}
</div>
</div>
</template>
dataTransfer.types
DataTransfer.types 是只读属性。它返回一个我们在dragstart事件中设置的拖动数据格式 (如 字符串,通常是MIME类型 的数组。格式顺序与拖动操作中包含的数据顺序相同。
下面是一个例子,通过检查dataTransfer属性的类型,决定是否允许在当前节点执行drop操作。
function contains(list, value){
for (var i = 0; i < list.length; ++i) {
if(list[i] === value) return true;
}
return false;
}
function doDragOver(event) {
var isLink = contains(event.dataTransfer.types, 'text/uri-list');
if (isLink) event.preventDefault();
}
上面代码中,只有当被拖拉的节点有一个是链接时,才允许在当前节点放下。
dataTransfer.items
DataTransfer.items属性返回一个类似数组的只读对象(DataTransferItemList 实例),每个成员就是本次拖拉的一个对象(DataTransferItem 实例)。如果本次拖拉不包含对象,则返回一个空对象。
DataTransferItemList 实例具有以下的属性和方法:
- length:返回成员的数量
- add(data, type):增加一个指定内容和类型(比如text/html和text/plain)的字符串作为成员
- add(file):add方法的另一种用法,增加一个文件作为成员
- remove(index):移除指定位置的成员
- clear():移除所有的成员
DataTransferItem 实例具有以下的属性和方法。
- kind:返回成员的种类(string还是file)。
- type:返回成员的类型(通常是 MIME类型 值)。
- getAsFile():如果被拖拉是文件,返回该文件,否则返回null。
- getAsString(callback):如果被拖拉的是字符串,将该字符传入指定的回调函数处理。该方法是异步的,所以需要传入回调函数。
5.dataTransfer对象方法
dataTransfer.setData()
DataTransfer.setData()方法用来设置拖拉事件所带有的数据。该方法没有返回值。
// setData()传递数据一般绑定在被拖拽元素的dragstart事件中
drag?.addEventListener('dragstart', (e) => {
e.dataTransfer.setData('text/plain', '给我一个div')
})
- 上面代码为当前的拖拉事件加入纯文本数据。
该方法接受两个参数,都是字符串。第一个参数表示MIME类型(比如text/plain表示纯文本),第二个参数是具体数据.
如果给定类型的数据不存在,则将其添加到拖动数据存储的末尾,使得
types
列表中的最后一个项目将是新类型。如果给定类型的数据已经存在,现有数据将被替换为相同的位置。也就是说,替换相同类型的数据时
types
列表的顺序不会更改。
- 添加其他类型的数据:
const dt = event.dataTransfer;
// 添加链接
dt.setData('text/uri-list', 'http://www.example.com');
// 添加 HTML 代码
dt.setData('text/html', 'Hello there, <strong>stranger</strong>');
// 添加图像的 URL
dt.setData('text/uri-list', imageurl);
备注:setData()
传递数据一般绑定在被拖拽元素的dragstart
事件中
dataTransfer.getData()
DataTransfer.getData()方法接受一个字符串(表示数据类型)作为参数,返回事件所带的指定类型的数据(通常是用setData方法添加的数据)。如果指定类型的数据不存在,则返回空字符串。通常只有drop事件触发后,才能取出数据。
下面是一个drop事件的监听函数,用来取出指定类型的数据。
function onDrop(event) {
const data = event.dataTransfer.getData('text/plain');
event.target.textContent = data;
event.preventDefault();
}
上面代码取出拖拉事件的文本数据,将其替换成当前节点的文本内容。注意,这时还必须取消浏览器的默认行为,因为假如用户拖拉的是一个链接,浏览器默认会在当前窗口打开这个链接。
getData
方法返回的是一个字符串,如果其中包含多项数据,就必须手动解析。
function doDrop(event) {
const lines = event.dataTransfer.getData('text/uri-list').split('\n');
for (let line of lines) {
let link = document.createElement('a');
link.href = line;
link.textContent = line;
event.target.appendChild(link);
}
event.preventDefault();
}
类型值指定为URL,可以取出第一个有效链接。
const link = event.dataTransfer.getData('URL');
下面的例子是从多种类型的数据里面取出数据。
function doDrop(event) {
let types = event.dataTransfer.types;
cosnt supportedTypes = ['text/uri-list', 'text/plain'];
types = supportedTypes.filter((value)=> { return types.includes(value) });
if (types.length) {
const data = event.dataTransfer.getData(types[0]);
}
event.preventDefault();
}
dataTransfer.clearData()
DataTransfer.clearData()方法接受一个字符串(表示数据类型)作为参数,删除事件所带的指定类型的数据。如果没有指定类型,则删除所有数据。如果指定类型不存在,则调用该方法不会产生任何效果。
event.dataTransfer.clearData('text/uri-list');
上面代码清除事件所带的text/uri-list
类型的数据。 该方法不会移除拖拉的文件,因此调用该方法后,DataTransfer.types属性可能依然会返回Files类型(前提是存在文件拖拉)。
注意
该方法只能在dragstart事件的监听函数之中使用,因为这是拖拉操作的数据唯一可写的时机。
dataTransfer.setDragImage()
拖动过程中(dragstart事件触发后),浏览器会显示一张图片跟随鼠标一起移动,表示被拖动的节点。这张图片是自动创造的,通常显示为被拖动节点的外观,不需要自己动手设置。
DataTransfer.setDragImage()
方法可以自定义这张图片。它接受三个参数。第一个是<img>
节点或者<canvas>
节点,如果省略或为null,则使用被拖动的节点的外观;第二个和第三个参数为鼠标相对于该图片左上角的横坐标和纵坐标。
<script setup lang='ts'>
import { onMounted } from 'vue'
onMounted(() => {
const drag = document.querySelector('.drag') as any
const dragImg = document.querySelector('img')
drag?.addEventListener('dragstart', (e) => {
e.dataTransfer.setDragImage(dragImg, 20, 50)
})
})
</script>
<template>
<img
src="./assets/lbxx.jpg" width="100" height="100"
class="m-auto block rounded-50%"
>
<div class="drag m-auto mt20px h100px w100px bg-sky text-red" draggable="true">
拖动我
</div>
</template>