Skip to content

拖拽事件

1. 拖拽事件类型

事件名触发时机绑定对象
dragstart用户开始拖动 HTML 元素或选中的文本拖拽元素
drag正在拖动元素或文本选区(在此过程中持续触发,每 350ms 触发一次)拖拽元素
dragend拖放操作结束(松开鼠标按钮或按下 Esc 键)拖拽元素
dragenter被拖动的元素或文本选区移入有效释放目标区目标元素
dragover被拖动的元素或文本选区正在有效释放目标上被拖放(在此过程中持续触发,每 350ms 触发一次)目标元素
dragleave被拖动的元素或文本选区移出有效释放目标区目标元素
drop元素在有效释放目标区上释放目标元素

2. 拖拽条件

  1. 被拖拽元素 draggble属性为 true
html
 <!-- p标签可以被拖动 -->
<p draggble = "true">一个div</p>
  1. 目标元素可被接收被拖拽元素释放=====> 一般在dragover事件中阻止事件默认行为
typescript
<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:无法放下被拖拉的节点

    typescript
    
    target.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
typescript
source.addEventListener('dragstart', function (e) {
  e.dataTransfer.effectAllowed = 'move';
});

target.addEventListener('dragover', function (e) {
  e.dataTransfer.dropEffect = 'move';
});

关于effectAlloweddropEffect:

  1. effectAllowed : 相当于总规则 允许使用哪些效果 dropEffect为具体的效果 dropEffect的值 必须被包含 在effectAllowed设置的值内

  2. 只要dropEffect属性和effectAllowed属性之中,有一个为none,就无法在目标节点上完成 drop 操作

  3. 感觉没什么用 不知道是自己没了解真正的用途还是怎么 拿到就是为了设置个鼠标样式么


dataTransfer.files

DataTransfer.files属性是一个 FileList 对象,包含一组本地文件,可以用来在拖拉操作中传送。如果本次拖拉不涉及文件,则该属性为空的 FileList 对象。

示例:

+
拖拽一个文件来试试

vue
<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()方法用来设置拖拉事件所带有的数据。该方法没有返回值。

javaScript
//  setData()传递数据一般绑定在被拖拽元素的dragstart事件中
 drag?.addEventListener('dragstart', (e) => {
    e.dataTransfer.setData('text/plain', '给我一个div')
 })
  1. 上面代码为当前的拖拉事件加入纯文本数据。
  • 该方法接受两个参数,都是字符串。第一个参数表示MIME类型(比如text/plain表示纯文本),第二个参数是具体数据.

  • 如果给定类型的数据不存在,则将其添加到拖动数据存储的末尾,使得 types 列表中的最后一个项目将是新类型。

  • 如果给定类型的数据已经存在,现有数据将被替换为相同的位置。也就是说,替换相同类型的数据时 types列表的顺序不会更改。

  1. 添加其他类型的数据:
javaScript
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事件的监听函数,用来取出指定类型的数据。

javaScript
function onDrop(event) {
  const data = event.dataTransfer.getData('text/plain');
  event.target.textContent = data;
  event.preventDefault();
}

上面代码取出拖拉事件的文本数据,将其替换成当前节点的文本内容。注意,这时还必须取消浏览器的默认行为,因为假如用户拖拉的是一个链接,浏览器默认会在当前窗口打开这个链接。

getData方法返回的是一个字符串,如果其中包含多项数据,就必须手动解析。

javaScript
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,可以取出第一个有效链接。

javaScript
const link = event.dataTransfer.getData('URL');

下面的例子是从多种类型的数据里面取出数据。

javaScript
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()方法接受一个字符串(表示数据类型)作为参数,删除事件所带的指定类型的数据。如果没有指定类型,则删除所有数据。如果指定类型不存在,则调用该方法不会产生任何效果。

javaScript
 event.dataTransfer.clearData('text/uri-list');

上面代码清除事件所带的text/uri-list类型的数据。 该方法不会移除拖拉的文件,因此调用该方法后,DataTransfer.types属性可能依然会返回Files类型(前提是存在文件拖拉)。

注意

该方法只能在dragstart事件的监听函数之中使用,因为这是拖拉操作的数据唯一可写的时机。

dataTransfer.setDragImage()

拖动过程中(dragstart事件触发后),浏览器会显示一张图片跟随鼠标一起移动,表示被拖动的节点。这张图片是自动创造的,通常显示为被拖动节点的外观,不需要自己动手设置。

DataTransfer.setDragImage()方法可以自定义这张图片。它接受三个参数。第一个是<img>节点或者<canvas>节点,如果省略或为null,则使用被拖动的节点的外观;第二个和第三个参数为鼠标相对于该图片左上角的横坐标和纵坐标。

vue
<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>