HTML5 拖放

HTML5 Drag and Drop
全面理解Drag & Drop API

元素的拖放(Drag and Drop)在Web页面上也是一个常见的特性,即抓取一个对象拖动并将其放到另一个位置;HTML5将其标准化:任何页面元素都可以被拖动。

HTML5拖放的浏览器支持情况如下:

API Chrome IE Firefox Safari Opera
Drag and Drop 4.0 9.0 3.5 6.0 12.0

draggable 属性

draggable 属性是HTML5新增的一个属性,指定一个元素是否可以被拖动,所有HTML元素都可以设置该属性;draggable 属性可以设置的值如下所示:

  • true:元素可以拖动
  • false:元素不可以拖动
  • auto:使用浏览器默认行为

默认情况下,图片和链接两种元素是可以拖动的

下面两个段落中的第二个将 draggable 属性设置为true后,可以看到该段落可以拖动了,鼠标移到上面后指针也不再变化,只是好像不能再选择文字了。

不可拖动的段落

可以拖动的段落

拖放相关事件

在元素的拖放过程会触发一系列事件,我们正是使用这些事件来响应拖放操作的;这些事件可以分为两类:拖动元素(拖放源)事件和放置元素(拖放目标)事件。

被拖动元素上触发的事件

  • dragstart:用户开始拖动一个元素时触发
  • drag:元素正在被拖动时触发
  • dragend:用户结束元素拖动时触发

触发dragstart事件后,其他元素的鼠标事件诸如 mousemove、mouseover、mouseout 等事件均不会被触发了。

注意,拖动元素时,drag事件每350毫秒触发一次

放置元素上触发的事件

  • dragenter:当被拖动元素进入目标元素时触发
  • dragover:被拖动元素在目标元素上移动时触发
  • dragleave:被拖动元素离开目标元素时触发
  • drop:被拖动元素放在目标元素时触发(在目标元素上松开鼠标)

dragover事件是被拖拽元素在目标元素上移动一段时间后才触发,因此该事件并不会与dragenter事件一起被触发。

从上面的解释可以了解到,一个拖放过程应该是这样的:dragstart –> dragenter –> dragover –> drop –> dragend

DataTransfer 对象

DataTransfer 对象用于配置拖拽行为效果,并且在拖拽过程的各事件间传递数据,传递的数据类型被限定为字符串和文件类型;DataTransfer 对象作为一个属性存储在拖拽事件对象(DragEvent)中,DragEvent继承自MouseEvent,其实就比MouseEvent多了一个 DataTransfer 对象属性。

DataTransfer的属性

effectAllowed

effectAllowed 属性用于设置被拖拽元素可执行的操作。

该属性可以设置的值如下:

可用值 说明
copy 限定dropEffect的属性值为copy,否则会鼠标指针为禁止样式
link 限定dropEffect的属性值为link,否则会鼠标指针为禁止样式
move 限定dropEffect的属性值为move,否则会鼠标指针为禁止样式
copyLink 限定dropEffect的属性值为copy和link,否则会鼠标指针为禁止样式
copyMove 限定dropEffect的属性值为copy和move,否则会鼠标指针为禁止样式
linkMove 限定dropEffect的属性值为link和move,否则会鼠标指针为禁止样式
all 允许dropEffect的属性值为任意值
none 鼠标指针一直为禁止样式,不管dropEffect的属性值是什么
uninitialized 没有限定dropEffect属性的值,效果和 all 一样

仅能在 dragstart 事件中设置该属性,其他事件中设置均无效。

dropEffect

dropEffect 属性用于设置目标元素将执行的操作,若属性值属于 effectAllowed 范围内,则鼠标指针将显示对应的指针样式,否则则显示禁止的指针样式。

该属性的取值及意义如下:

可用值 说明
copy 被拖拽元素将被复制到目标元素内,若属于 effectAllowed 范围内时,则鼠标指针显示复制的样式,否则则显示禁止的指针样式。
link 被拖拽元素将以超链接的形式打开资源,若属于 effectAllowed 范围内时,则鼠标指针显示超链接的样式,否则则显示禁止的指针样式。
move 被拖拽元素将被移动到目标元素内,若属于 effectAllowed 范围内时,则鼠标指针显示移动的样式,否则则显示禁止的指针样式。
none 被拖拽元素不能在目标元素上作任何操作,一直显示禁止的指针样式。除了文本框外其他元素的默认值均为none

仅能在 dragover 事件中设置该属性值,其他事件中设置均无效
当显示禁止的指针样式时,将无法触发目标元素的 drop 事件。

items

items 属性数据类型为 DataTransferItemList,存储 DataTransfer 对象中所有的数据项。目前只有Chrome浏览器支持该属性。

DataTransferItemList 是个类数组的类型,可以使用索引引用数据项,也有length属性;另外还有clear()和add()方法。

files

files 属性数据类型为FileList

types

types 属性数据类型为DOMStringList,存储DataTransfer对象中所有数据项的数据类型。

仅能在dragenter,dragover和drop中获取该属性

DataTransfer的方法

addElement()

void addElement({HTMLElement} element) 方法用于添加一起跟随鼠标移动的元素。

addElement() 方法仅在 dragstart 事件中可以调用。

setDragImage()

void setDragImage({Element} image, {long} x, {long} y) 方法用于设置拖动时跟随鼠标移动的图片,用来替代默认的元素;若image参数不是图片元素则会将元素临时转换为图片;x和y分别用于设置图标与鼠标指针在水平方向和垂直方向上的距离。

setDragImage() 方法仅在 dragstart 事件中可以调用。

image参数必须在DOM树中,而且在渲染树中为有效元素,即display不为none,否则会导致没有元素跟随鼠标移动

setData()

boolean setData({DOMString} format, {DOMString} data) 方法将指定格式的数据赋值给dataTransfer或clipboardData,format值范围为URL、Text(或text)和各种MIME类型,其实Text会被自动映射为text/plain,URL会被自动映射为text/uri-list类型。

setData() 方法仅在 dragstart 事件中可以调用。

当没有填写第二个入参时,则会根据format来删除相应的数据项

getData()

DOMString getData({DOMString} format) 方法从DataTransfer对象或ClipboardData对象中获取指定格式的数据。

clearData()

void clearData([{DOMString} format]) 方法从DataTransfer对象或ClipboardData对象中删除指定格式或全部kind值为string的数据。

仅在 dragstart 事件中调用,在其他事件中调用会抛InvalidStateError。

数据存储模式

  • Read/Write(读写)模式:dragstart 事件为该模式,可读写数据
  • Read-only(只读)模式:在 drop 事件为该模式,仅能读取数据
  • Protected(保护)模式:其他事件为该模式,仅能枚举数据

示例

简单的拖放

下面是一个简单的元素拖放示例,仅仅是将一个p元素拖到div元素中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(function () {
let p = document.getElementById('drag-p');
let div = document.getElementById('div-1');
p.addEventListener('dragstart', function (e) {
e.dataTransfer.setData('text', e.target.id);
});
div.addEventListener('dragover', function(e) {
e.preventDefault();
});
div.addEventListener('drop', function (e) {
e.preventDefault();
let id = e.dataTransfer.getData('text');
let dragEl = document.getElementById(id);
if (dragEl instanceof Node) {
dragEl.removeAttribute('draggable');
dragEl.innerHTML = '我不允许再拖动了噢';
e.target.appendChild(dragEl);
}
});
})();

把我拖到下面方框试试

首先给p元素添加一个 dragstart 事件监听,在监听函数中向 DataTransfer 对象设置了Text类型的数据,值是被拖动元素的ID。

接下来给目标元素添加了 dragover 事件监听,默认情况下,无法将数据/元素放置到其他元素中,如果设置为允许放置,我们必须阻止对元素的默认处理方式,也就是调用 DragEvent 事件对象的 preventDefault() 方法。

最后又给目标元素设置了 drop 事件监听,用来响应拖动元素放下的动作;调用事件对象的 preventDefault() 方法来避免浏览器对数据的默认处理(drop 事件的默认行为是以链接形式打开);接着获取存在 DataTransfer 对象中的数据(拖动元素的ID),然后将该元素追加到目标元素中。

复制元素

接下来再写一个示例,也很简单,与上一个示例区别就是这次不是移动元素,而是将元素复制一份加入目标元素,拖动过程中注意看鼠标指针的变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
(function () {
let p = document.getElementById('drag-p2');
let div = document.getElementById('div-2');
p.addEventListener('dragstart', function (e) {
e.dataTransfer.setData('text', e.target.id);
e.dataTransfer.effectAllowed = 'copyMove';
});
div.addEventListener('dragover', function(e) {
e.preventDefault();
e.dataTransfer.dropEffect = 'copy';
});
div.addEventListener('drop', function (e) {
e.preventDefault();
let id = e.dataTransfer.getData('text');
let dragEl = document.getElementById(id);
if (dragEl instanceof Node) {
let cloneEl = dragEl.cloneNode(true);
cloneEl.removeAttribute('draggable');
cloneEl.innerHTML = '我是不允许拖动的噢';
e.target.appendChild(cloneEl);
}
});
})();

把我拖到下面方框试试

代码与上一个示例相差无几,主要区别在于以下两点:

  1. dragstart 事件监听函数中将 DataTransfer 对象的 effectAllowed 属性设置为 copyMove;
  2. dragover 事件监听函数中将 DataTransfer 对象的 dropEffect 属性设置为copy;

这样就可以在拖拽元素拖到目标元素上时改变鼠标指针,最后在 drop 事件监听函数中复制一份拖拽元素并加入目标元素中。

文章目录
  1. 1. draggable 属性
  2. 2. 拖放相关事件
  3. 3. DataTransfer 对象
    1. 3.1. DataTransfer的属性
      1. 3.1.1. effectAllowed
      2. 3.1.2. dropEffect
      3. 3.1.3. items
      4. 3.1.4. files
      5. 3.1.5. types
    2. 3.2. DataTransfer的方法
      1. 3.2.1. addElement()
      2. 3.2.2. setDragImage()
      3. 3.2.3. setData()
      4. 3.2.4. getData()
      5. 3.2.5. clearData()
    3. 3.3. 数据存储模式
  4. 4. 示例
    1. 4.1. 简单的拖放
    2. 4.2. 复制元素
|