前端高级-前端拖拽技术

传统拖拽方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 简单实现一个拖拽
function oDrag(handle) {
const el = document.querySelector(handle);
let _ox, _oy;
el.addEventListener('mousedown', handleMouseDown, false);
function handleMouseDown(e) {
_ox = e.offsetX;
_oy = e.offsetY;
document.addEventListener('mousemove', handleMouseMove, false);
document.addEventListener('mouseup', handleMouseUp, false);
}
function handleMouseMove(e) {
const left = e.clientX - _ox;
const top = e.clientY - _oy;
el.style.left = left + 'px';
el.style.top = top + 'px';
}
function handleMouseUp(e) {
document.removeEventListener('mousemove', handleMouseMove, false);
document.removeEventListener('mouseup', handleMouseUp, false);
}
}

H5 Drag&Drop API

开始拖拽

设置 draggable 属性,监听 dragstart 事件。

draggable 属性

所有 HTML 元素都可以设置 draggable 属性。

取值:true、false、auto(取用户代理默认值,一般情况下选中文本、图片、链接默认可拖拽)。

具有 draggable 属性的元素还应该具有 title 属性,该标题属性用于非视觉交互的目的。

拖拽数据

event.dataTransfer接口

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
[Exposed=Window,Constructor] // 暴露给 window 对象,
interface DataTransfer {
/** dropEffect
返回当前选择的操作类型。如果该操作类型不是 effectAllowed 属性所允许的一种操作,则该操作将失败。
可以设置,以更改所选的操作。
可能的值为 “none”,“copy”,“link” 和 “move”(设置其他值将被忽略)。
*/
attribute DOMString dropEffect;
/** effectAllowed
返回允许的操作类型。
可以设置(在dragstart事件期间),以更改允许的操作。
可能的值为 “none”,“copy”,“copyLink”,“copyMove”,“link”,“linkMove”,“move”,“all” 和 “uninitialized”。
*/
attribute DOMString effectAllowed;
/** items
返回带有拖动数据的 DataTransferItemList 对象。
*/
[SameObject] readonly attribute DataTransferItemList items;
/** setDragImage
使用给定的元素更新拖动反馈,以替换任何先前指定的反馈。
*/
void setDragImage(Element image, long x, long y);

/* old interface */
/** types
返回一个冻结的数组,列出在 dragstart 事件中设置的格式。此外,如果要拖动任何文件,则类型之一将是字符串 “Files”。
*/
readonly attribute FrozenArray<DOMString> types;
/** getData
返回指定的数据。如果没有这样的数据,则返回空字符串。
只能在 drop 事件中获取。
*/
DOMString getData(DOMString format);
/** setData
添加指定的数据。
只能在 dragstart 事件中设置。
*/
void setData(DOMString format, DOMString data);
/** clearData
删除指定格式的数据。如果省略该参数,则删除所有数据。
*/
void clearData(optional DOMString format);
/** files
返回要拖动的文件的FileList(如果有)。
*/
[SameObject] readonly attribute FileList files;
};

拖拽事件

事件名 e.target 是否可取消 数据存储模式 dropEffect 默认行为
dragstart 源节点 可取消 read/write “none” 启动拖放操作
drag 源节点 可取消 protected “none” 继续拖放操作
dragenter 用户直接指定的可drop元素或body元素 可取消 protected 取决于 effectAllowed 拒绝用户直接指定的可drop元素作为潜在目标元素
dragexit 上一个目标元素 - protected “none” None
dragleave 上一个目标元素 - protected “none” None
dragover 当前目标节点 可取消 protected 取决于 effectAllowed 将当前拖动操作重置为”none”
drop 当前目标节点 可取消 read 当前拖动操作 不定
dragend 源节点 - protected 当前拖动操作 不定

说明:

  • 是否可取消:e.cancelable只读属性,如果事件不可取消,则其cancelable属性将为false,并且事件侦听器无法停止事件的发生;要取消事件,请对该事件调用preventDefault()方法;取消后事件相当于没发生一样。
  • DragEvent 对象,继承自 MouseEvent,只多了一个独有的 DataTransfer 属性。
  • dragenter 只在进入可 drop 元素时触发一次;dragover 只要没离开可 drop 元素,就会一直触发。
  • dragleave 与 dragenter 对应。
  • dragexit:当元素不再是拖动操作的立即选择目标时,将触发dragexit事件。(????)
  • 所有这些事件都会冒泡,并且 effectAllowed 属性始终具有 dragstart 事件之后的值,在 dragstart 事件中默认为“uninitialized”。

结束拖拽

当拖动一个项目到 HTML 元素中时,浏览器默认不会有任何响应。想要让一个元素变成可释放区域,该元素必须设置 dragover 和 drop 事件处理程序属性。且 dragover 监听时要调用 e.preventDefault()(注意每个处理程序调用 preventDefault() 来阻止对这个事件的其它处理过程)。

在网页中,如果监听了 drop 事件,应该调用事件的 preventDefault() 来阻止默认操作。例如,当将链接拖到网页上时,Firefox将打开该链接。通过取消事件,将防止此行为。

dragend 事件:源节点上触发。不管拖动是完成还是被取消这个事件都会被触发。

drop 事件:目标节点上触发。该元素必须设置 dragover 和 drop 事件处理程序属性,且阻止了 dragover 默认行为才会触发。

1
2
3
4
5
6
7
8
function dragOverHandler(e) {
e.preventDefault();
}

function dropHandler(e) {
e.preventDefault();
const data = e.dataTransfer.getData('text/plain');
}

第三方拖拽库

https://github.com/SortableJS/Sortable

https://github.com/SortableJS/Vue.Draggable

参考