HTML5拖放API Drag and Drop

Leechikit

此文研究Web API中的拖放接口,提供各个属性和方法的说明,解决拖放过程中的拖拽数据对象存储和获取问题。

拖放API作用到两个目标对象,分别是拖拽目标对象和放置目标对象。

拖拽目标

一个设置draggable属性的值为trueDOM元素或者一个选中状态的文本区块可以成为拖拽目标。

<div draggable="true"></div>

OR
yy 20170629201338

放置目标

一个绑定了下图放置目标对应的5个事件的DOM元素可以成为放置目标。

事件

拖放API有8个事件,其中有3个事件绑定在拖拽目标上,有5个事件绑定在放置目标上。

绑定在拖拽目标

Evnet Description
dragstart 当用户开始拖拽一个元素或者一个文本选取区块的时触发。
drag 当用户正在拖拽一个元素或者一个文本选取区块的时触发。
dragend 当用户结束拖拽一个元素或者一个文本选取区块的时触发。(如放开鼠标按键或按下键盘的 escap 键)

绑定在放置目标

Event Description
dragenter 当一个元素或文字选取区块被拖曳移动进入一个有效的放置目标时触发。
dragover 当一个元素或文字选取区块被拖曳移动经过一个有效的放置目标时触发。
dragleave 当一个元素或文字选取区块被拖曳移动离开一个有效的放置目标时触发。
dragexist 当一个元素不再是被选取中的拖曳元素时触发。(Firefox能触发,触发顺序:dragexist->dragleave->drop;Chrome无法触发)
drop 当一个元素或文字选取区块被放置至一个有效的放置目标时触发。

通过下图能更直观观察每个事件触发的时机

dragdrop-events

戳我看源码

注意:在dragover事件中使用event.preventDefault();阻止默认事件,才能触发drop事件

DataTransfer对象

在进行拖放操作时,会触发上面所述的8个事件,每个event事件对象中都会有DataTransfer对象用来保存被拖动的数据。它可以保存一项或多项数据、一种或者多种数据类型。

effectAllowed

用来指定拖动时被允许的效果。

dragstart事件中设置

属性

dropEffect

设置实际的放置效果,它应该始终设置成effectAllowed的可能值之一 。

dragenter事件和dragover事件中设置

effectAllowed和dropEffect属性的栗子:戳我看源码

files

包含一个在数据传输上所有可用的本地文件列表。如果拖动操作不涉及拖动文件,此属性是一个空列表。

filesZoneEl.addEventListener("drop", (event) => {
    event.preventDefault();
    let files = event.dataTransfer.files;
    for (let i = 0, len = files.length; i < len; i++) {
        let liEl = document.createElement("li");
        liEl.innerHTML = files[i].name;
        filesListEl.appendChild(liEl);
    }
});

drag-file

戳我看源码

types

保存一个被存储数据的类型列表作为第一项,顺序与被添加数据的顺序一致。如果没有添加数据将返回一个空列表。

items

存储DataTransferItem数据对象的列表。

方法

addElement()

设置拖动源。

event.dataTransfer.addElement(element);

setData()

为一个给定的类型设置数据并存储在items属性中。

getData()

items属性中获取给定类型的数据,无数据时返回空字符串。

event.dataTransfer.getData(type);

clearData()

items属性中删除与给定类型关联的数据,若类型为空则删除所有数据。

event.dataTransfer.clearData(type);

setDragImage()

自定义一个期望的拖动时的图片,默认为被拖动的节点。

event.dataTransfer.setDragImage(imgElement, offsetX, offsetY);
Param Description
imgElement 要用作拖动反馈图像元素。
offsetX 图像内的水平偏移量。
offsetY 图像内的垂直偏移量。

设置拖动时的图片时,要把图片预加载,否则图片会在拖动开始dragstart事件触发时才会加载图片,会导致拖动图出不来或闪一下的后果。可把图片放到<img>标签并设置display:none;,原理详看我之前的文章Web图片资源的加载与渲染时机

drag-imgage

DataTransferItemList

dataTramsfer对象的items属性,包含了一系列DataTransferItem拖拽数据对象。

属性

length

数组长度。

方法

add()

增加一个拖拽数据对象到items属性中,并返回增加的拖拽数据对象。

event.dataTransfer.items.add(file);

remove()

items属性中移除指定位置的一个拖拽数据对象。

event.dataTransfer.items.remove(index);

clear()

清空items属性中的所拖拽数据对象。

event.dataTransfer.items.clear();

DataTransferItem

DataTransferItemList列表中的拖拽数据对象。

属性

kind

拖拽数据对象类型。

Value Description
file 文件类型。
string 文本字符串类型。

type

MIME类型的Unicode字符串,例如text/plaintext/htmlimage/png

方法

getAsFile()

若拖拽数据对象是文件类型,则返回一个文件对象。

let itemList = event.dataTransfer.items;
for (let i = 0, len = itemList.length; i < len; i++) {
    if (itemList[i].kind == "file") {
        console.log(itemList[i].getAsFile());
    }
}

getAsString()

若拖拽数据对象是文本字符串类型,通过回调函数获取拖拽数据中的字符串数据。

let itemList = event.dataTransfer.items;
for (let i = 0, len = itemList.length; i < len; i++) {
    if (itemList[i].kind == "string") {
        itemList[i].getAsString((data) => {
            console.log(data);
        });
    }
}

拖放对象的数据存储

在进行拖放操作时,有可能需要把拖拽目标的数据传送给放置目标,此时一般操作是在dragstart事件触发时把需要的数据存储到一个变量,然后再drop事件触发时获取这个变量。但当dragstart事件和drop事件在不同的文件定义,又不想玷污全局变量的情况下,我们需要更好的办法来存储拖放数据。

DataTransfer对象中的items属性就是用来存储拖放数据的,数据类型分为文本类型和文件类型。

存储文本字符串类型数据:

event.dataTransfer.setData(type, data);

OR

event.dataTransfer.items.add(data, type);

一种文本字符串类型只能存储一个数据,当重复文本字符串类型存储数据时,后者会覆盖前者。

存储文件类型数据:

event.dataTransfer.items.add(file);

获取所有文本字符串类型的拖拽数据对象

event.dataTransfer.types

获取所有文件类型的拖拽数据对象

let files = event.dataTransfer.files;
for (let i = 0, len = files.length; i < len; i++) {
    console.log(files[i]);
}

OR

let itemList = event.dataTransfer.items;
for (let i = 0, len = itemList.length; i < len; i++) {
    if (itemList[i].kind == "file") {
        console.log(itemList[i].getAsFile());
    }
}

获取所有文本字符串类型的拖拽数据对象

let itemList = event.dataTransfer.items;
for (let i = 0, len = itemList.length; i < len; i++) {
    if (itemList[i].kind == "string") {
        itemList[i].getAsString((data) => {
            console.log(data);
        });
    }
}

获取指定文本字符串类型的拖拽数据对象

event.dataTransfer.getData(type);

删除指定文本字符串类型的拖拽数据对象

event.dataTransfer.clearData(type);

删除指定位置的拖拽数据对象

event.dataTransfer.items.remove(index);

清空所有拖拽数据对象

event.dataTransfer.clearData();

OR

event.dataTransfer.items.clear();

栗子

上面的几个栗子都使用了以上方法存储和获取拖拽数据对象,感兴趣的可以看看源码。

欢迎关注:Leechikit
原文链接:segmentfault.com

到此本文结束,欢迎提问和指正。
写原创文章不易,若本文对你有帮助,请点赞、推荐和关注作者支持。

阅读 11.9k

Leechikit的专栏
记录自己前端职业之路的积累与成长
2k 声望
113 粉丝
0 条评论
你知道吗?

2k 声望
113 粉丝
文章目录
宣传栏