HTML Drag and Drop API - 使应用程序能够在浏览器中使用拖放功能
HTML 拖放接口使应用程序能够在浏览器中使用拖放功能。用户可以使用鼠标选择可拖动元素,将这些元素拖动到可放置元素,然后通过释放鼠标按钮来放置它们。在拖动操作过程中,可拖动元素会以半透明的表现形式会跟随指针移动。
对于网站、扩展和 XUL 应用程序,您可以自定义哪些元素可以变为可拖动元素、可拖动元素产生的反馈类型以及可放置元素。
此 HTML 拖放概述包括接口说明、向应用程序添加拖放支持的基本步骤以及接口的互操作性摘要。
拖动事件
HTML 拖放使用DOM 事件模型和从鼠标事件继承的拖动事件。典型的拖动操作开始于用户选择可拖动元素,将元素拖动到可放置元素,然后释放被拖动元素。
在拖动操作期间,会触发多种事件类型,并且某些事件可能会触发多次,例如 drag 和 dragover 事件。
| 事件 | 事件处理程序 | 触发时机 | 
|---|---|---|
| drag | ondrag | 当拖动的项目(元素或选择的文本)被拖动。 | 
| dragend | ondragend | 当拖动操作结束(例如释放鼠标按钮或按 Esc 键;请参阅完成拖动。) | 
| dragenter | ondragenter | 当拖动的项目进入有效的放置目标。(请参阅指定放置目标。) | 
| dragexit | ondragexit | 当元素不再是拖动操作的直接选择目标。 | 
| dragleave | ondragleave | 当拖动的项目离开有效的放置目标时。 | 
| dragover | ondragover | 当拖动的项目被拖动到有效的放置目标上时,每次隔几百毫秒。 | 
| dragstart | ondragstart | 当用户开始拖动项目时。(请参阅开始拖动操作。) | 
| drop | ondrop | 当一个项目被放置在一个有效的放置目标上时。(请参阅执行放置。) | 
注意: 将文件从操作系统拖入浏览器时,不会触发 dragstart 和 dragend 事件。
接口
HTML 拖放接口包含了 DragEvent、DataTransfer、DataTransferItem 和 DataTransferItemList.
DragEvent 接口有一个构造函数和一个 dataTransfer 属性,它是一个 DataTransfer 对象。
DataTransfer 对象包括拖动事件的状态,例如正在完成的拖动类型(如 copy 或 move)、拖动的数据(一个或多个项目)和每个拖动项目的 MIME 类型。DataTransfer 对象还具有向拖动数据添加或删除项目的方法。
DragEvent 和 DataTransfer 接口应该是向应用程序添加 HTML 拖放功能所需的唯一接口。(Firefox 向 DataTransfer 对象添加了一些特定的扩展,注意这些扩展仅适用于 Firefox。)
每个 DataTransfer 对象都包含一个 items 属性,它是一个包含 DataTransferItem 对象的列表。一个 DataTransferItem 对象代表一个拖动项目,每个对象都有一个 kind 属性(string 或 file)和一个 type 属性表示数据项的 MIME 类型。DataTransferItem 对象也有获取拖动项数据的方法。
DataTransferItemList 对象是一个包含 DataTransferItem 对象的列表。该对象具有向列表添加拖动项、从列表中删除拖动项以及清除所有拖动项的列表的方法。
DataTransfer 和 DataTransferItem 接口之间的主要区别在于前者使用同步 getData() 方法来访问拖动项的数据,但后者使用异步 getAsString() 方法。
注意: DragEvent 和 DataTransfer 在桌面浏览器上得到广泛支持。但是,DataTransferItem 和 DataTransferItemList 接口的浏览器支持有限。有关拖放互操作性的更多信息,请参阅互操作性。
特定于 Gecko 的接口
Mozilla 和 Firefox 支持标准拖放模型中没有的一些功能。这些是帮助拖动多个项目或非字符串数据(如文件)的便利功能。有关详细信息,请参阅 拖放多个项目。此外,请参阅 DataTransfer 参考页面,了解所有 Gecko 特有属性 和 Gecko 特有方法。
基础知识
本节总结了向应用程序添加拖放功能的基本步骤。
确定什么是可拖动
使元素可拖动,需要添加 draggable 属性和 ondragstart 全局事件处理程序,如以下代码实例所示:
<script>
  function dragstart_handler(ev) {
    // 将目标元素的 id 添加到数据传输对象
    ev.dataTransfer.setData("text/plain", ev.target.id);
  }
  window.addEventListener('DOMContentLoaded', () => {
    // 通过 id 获取元素
    const element = document.getElementById("p1");
    // 添加 ondragstart 事件监听器
    element.addEventListener("dragstart", dragstart_handler);
  });
</script>
<p id="p1" draggable="true">该元素是可拖动的。</p>
有关更多信息,请参阅:
定义拖动的数据
该应用程序可以自由地在拖动操作中包含任意数量的数据项。每个数据项都是一个特定 type 的 string —— 通常是一个 MIME 类型,例如 text/html。
每个拖动事件都有一个 dataTransfer 属性,用于保存事件的数据。这个属性(它是一个 DataTransfer 对象)也有管理拖动数据的方法。setData() 方法用于向拖动数据添加项目,如下例所示。
function dragstart_handler(ev) {
  // 添加不同类型的拖拽数据
  ev.dataTransfer.setData("text/plain", ev.target.innerText);
  ev.dataTransfer.setData("text/html", ev.target.outerHTML);
  ev.dataTransfer.setData("text/uri-list", ev.target.ownerDocument.location.href);
}
定义拖动的图像
默认情况下,浏览器提供在拖动操作期间出现在指针旁边的图像。但是,应用程序可以使用 setDragImage() 方法定义自定义图像,如下例所示。
function dragstart_handler(ev) {
  // 创建一个图像,然后将其用于拖动图像。
  // 注意: 将 "example.gif "改为真正的图像 URL,
  // 否则将不会创建图像,而会使用默认的拖动图像。
  let img = new Image();
  img.src = 'example.gif';
  ev.dataTransfer.setDragImage(img, 10, 10);
}
了解有关拖动反馈图像的更多信息:
定义拖动效果
dropEffect 属性用于控制在拖放操作期间向用户提供的反馈。它通常会影响浏览器在拖动时显示的光标。例如,当用户将鼠标悬停在放置目标上时,浏览器的光标可能会指示将发生的操作类型。
可以定义三种效果:
- copy表示将拖动的数据从其当前位置复制到放置位置。
- move表示拖动的数据将从当前位置移动到放置位置。
- link表示将在源位置和放置位置之间创建某种形式的关系或连接。
在拖动操作期间,可以修改拖动效果以指示在某些位置允许某些效果。
以下实例显示了如何使用此属性。
function dragstart_handler(ev) {
  ev.dataTransfer.dropEffect = "copy";
}
有关更多详细信息,请参阅:
定义一个放置区域
默认情况下,当把内容放到大多数 HTML 元素上时,浏览器不会有任何的效果。要更改该行为以使元素成为放置区域或可放置,该元素必须同时具有 ondragover 和 ondrop 事件处理程序属性。
以下实例显示了如何使用这些属性,并包括每个属性的基本事件处理程序。
<script>
function dragover_handler(ev) {
 ev.preventDefault();
 ev.dataTransfer.dropEffect = "move";
}
function drop_handler(ev) {
 ev.preventDefault();
 // 获取目标的 id 并将移动的元素添加到目标的 DOM
 const data = ev.dataTransfer.getData("text/plain");
 ev.target.appendChild(document.getElementById(data));
}
</script>
<p id="target" ondrop="drop_handler(event)" ondragover="dragover_handler(event)">放置区域</p>
请注意,每个处理程序都会调用 preventDefault() 以防止对此事件(例如 触摸事件 或 指针事件)进行额外的事件处理。
有关更多信息,请参阅:
处理放置效果
drop 事件的处理程序可以以特定于应用程序的方式自由处理拖动数据。
通常,应用程序使用 getData() 方法来获取拖动项,然后相应地处理它们。此外,应用程序语义可能因 dropEffect 的值和 / 或修饰键的状态而异。
以下实例显示了一个放置处理程序,它从拖动数据中获取源元素的 id,然后使用 id 将源元素移动到放置元素:
<script>
function dragstart_handler(ev) {
 // 将目标元素的 id 添加到数据传输对象
 ev.dataTransfer.setData("application/my-app", ev.target.id);
 ev.dataTransfer.effectAllowed = "move";
}
function dragover_handler(ev) {
 ev.preventDefault();
 ev.dataTransfer.dropEffect = "move"
}
function drop_handler(ev) {
 ev.preventDefault();
 // 获取目标的 id 并将移动的元素添加到目标的 DOM
 const data = ev.dataTransfer.getData("application/my-app");
 ev.target.appendChild(document.getElementById(data));
}
</script>
<p id="p1" draggable="true" ondragstart="dragstart_handler(event)">此元素是可拖动的。</p>
<div id="target" ondrop="drop_handler(event)" ondragover="dragover_handler(event)">放置区域</div>
有关更多信息,请参阅:
拖动结束
在拖动操作结束时,在源元素(即拖动开始的目标元素)上会触发 dragend 事件。
无论拖动是完成还是取消,都会触发此事件。dragend 事件处理程序可以检查 dropEffect 属性的值来确定拖动操作是否成功。
有关处理拖动操作结束的更多信息,请参阅:
互操作性
在 DataTransferItem 接口的浏览器兼容性表 中可以看出,桌面浏览器之间的拖放互操作性比较广泛(除了 DataTransferItem 和 DataTransferItemList 接口支持较少)。该数据还表明移动浏览器之间的拖放支持非常低。
实例和演示
- 使用 DataTransfer接口复制和移动元素
- 使用 DataTransferListItem接口复制和移动元素
- 拖放文件(仅限 Firefox):https://jsfiddle.net/9C2EF/
- 拖放文件(所有浏览器): https://jsbin.com/hiqasek/
- 使用拖放 API 的停车项目:https://park.glitch.me/(你可以在这里编辑)
规范
| 规范 | 状态 | 备注 | 
|---|---|---|
| HTML Living Standard | 现行的标准 | - |