1. 6.11 拖放
      1. 6.11.1 简介
      2. 6.11.2 拖放数据存储
      3. 6.11.3 DataTransfer 接口
        1. 6.11.3.1 DataTransferItemList 接口
        2. 6.11.3.2 DataTransferItem 接口
      4. 6.11.4 DragEvent 接口
      5. 6.11.5 处理模型
      6. 6.11.6 事件总结
      7. 6.11.7 draggable 属性
      8. 6.11.8 拖放模型中的安全风险

6.11 拖放

HTML_Drag_and_Drop_API

所有当前引擎都支持。

Firefox3.5+Safari3.1+Chrome4+
Opera12+Edge79+
Edge (旧版)18Internet Explorer5.5+
Firefox Android4+Safari iOS2+Chrome Android18+WebView Android4.4+Samsung Internet1.5+Opera Android14+

本节定义了一种基于事件的拖放机制。

本规范未定义拖放操作的具体含义。

在具有指向设备的可视媒体上,拖动操作可能是mousedown事件的默认操作,随后是一系列mousemove事件,并且释放鼠标可以触发放置操作。

当使用除指向设备之外的输入方式时,用户可能需要明确指示其执行拖放操作的意图,分别说明他们希望拖动什么以及希望将其放置在哪里。

无论如何实现,拖放操作都必须有一个起点(例如,鼠标单击的位置,或选择或元素开始拖动的位置),可以有任意数量的中间步骤(元素在拖动过程中鼠标移动到的元素,或用户在循环浏览可能性时选择为可能的放置点的元素),并且必须有一个终点(鼠标按钮释放上方的元素,或最终选择的元素),或者被取消。终点必须是在放置操作发生之前作为可能的放置点选择的最后一个元素(因此,如果操作未被取消,则中间步骤中必须至少有一个元素)。

6.11.1 简介

本节是非规范性的。

要使元素可拖动,请为该元素添加draggable属性,并为dragstart设置事件侦听器,以存储正在拖动的的数据。

事件处理程序通常需要检查它是否不是正在拖动的文本选择,然后需要将数据存储到DataTransfer对象中并设置允许的效果(复制、移动、链接或某种组合)。

例如

<p>What fruits do you like?</p>
<ol ondragstart="dragStartHandler(event)">
 <li draggable="true" data-value="fruit-apple">Apples</li>
 <li draggable="true" data-value="fruit-orange">Oranges</li>
 <li draggable="true" data-value="fruit-pear">Pears</li>
</ol>
<script>
  var internalDNDType = 'text/x-example'; // set this to something specific to your site
  function dragStartHandler(event) {
    if (event.target instanceof HTMLLIElement) {
      // use the element's data-value="" attribute as the value to be moving:
      event.dataTransfer.setData(internalDNDType, event.target.dataset.value);
      event.dataTransfer.effectAllowed = 'move'; // only allow moves
    } else {
      event.preventDefault(); // don't allow selection to be dragged
    }
  }
</script>

要接受放置,放置目标必须侦听以下事件

  1. dragenter事件处理程序通过取消事件报告放置目标是否可能愿意接受放置。
  2. dragover事件处理程序通过设置与事件关联的DataTransferdropEffect属性来指定将向用户显示的反馈。此事件也需要被取消。
  3. drop事件处理程序有最后一次机会接受或拒绝放置。如果接受放置,则事件处理程序必须对目标执行放置操作。此事件需要被取消,以便dropEffect属性的值可以被源使用。否则,放置操作将被拒绝。

例如

<p>Drop your favorite fruits below:</p>
<ol ondragenter="dragEnterHandler(event)" ondragover="dragOverHandler(event)"
    ondrop="dropHandler(event)">
</ol>
<script>
  var internalDNDType = 'text/x-example'; // set this to something specific to your site
  function dragEnterHandler(event) {
    var items = event.dataTransfer.items;
    for (var i = 0; i < items.length; ++i) {
      var item = items[i];
      if (item.kind == 'string' && item.type == internalDNDType) {
        event.preventDefault();
        return;
      }
    }
  }
  function dragOverHandler(event) {
    event.dataTransfer.dropEffect = 'move';
    event.preventDefault();
  }
  function dropHandler(event) {
    var li = document.createElement('li');
    var data = event.dataTransfer.getData(internalDNDType);
    if (data == 'fruit-apple') {
      li.textContent = 'Apples';
    } else if (data == 'fruit-orange') {
      li.textContent = 'Oranges';
    } else if (data == 'fruit-pear') {
      li.textContent = 'Pears';
    } else {
      li.textContent = 'Unknown Fruit';
    }
    event.target.appendChild(li);
  }
</script>

要从显示中删除原始元素(即被拖动的元素),可以使用dragend事件。

对于我们这里的示例,这意味着更新原始标记以处理该事件

<p>What fruits do you like?</p>
<ol ondragstart="dragStartHandler(event)" ondragend="dragEndHandler(event)">
 ...as before...
</ol>
<script>
  function dragStartHandler(event) {
    // ...as before...
  }
  function dragEndHandler(event) {
    if (event.dataTransfer.dropEffect == 'move') {
      // remove the dragged element
      event.target.parentNode.removeChild(event.target);
    }
  }
</script>

6.11.2 拖放数据存储

构成拖放操作基础的数据,称为拖放数据存储,包含以下信息

拖放数据存储创建时,必须将其初始化,使其拖放数据存储项列表为空,没有拖放数据存储默认反馈,没有拖放数据存储位图拖放数据存储热点坐标,其拖放数据存储模式保护模式,其拖放数据存储允许的效果状态为字符串“uninitialized”。

6.11.3 DataTransfer 接口

DataTransfer

所有当前引擎都支持。

Firefox3.5+Safari4+Chrome3+
Opera12+Edge79+
Edge (旧版)12+Internet Explorer8+
Firefox Android?Safari iOS?Chrome Android?WebView Android37+Samsung Internet?Opera Android12+

DataTransfer对象用于公开构成拖放操作基础的拖放数据存储

[Exposed=Window]
interface DataTransfer {
  constructor();

  attribute DOMString dropEffect;
  attribute DOMString effectAllowed;

  [SameObject] readonly attribute DataTransferItemList items;

  undefined setDragImage(Element image, long x, long y);

  /* old interface */
  readonly attribute FrozenArray<DOMString> types;
  DOMString getData(DOMString format);
  undefined setData(DOMString format, DOMString data);
  undefined clearData(optional DOMString format);
  [SameObject] readonly attribute FileList files;
};
dataTransfer = new DataTransfer()

DataTransfer/DataTransfer

所有当前引擎都支持。

Firefox62+Safari14.1+Chrome59+
Opera?Edge79+
Edge (旧版)17+Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet8.0+Opera Android44+

使用空拖放数据存储创建一个新的DataTransfer对象。

dataTransfer.dropEffect [ = value ]

DataTransfer/dropEffect

所有当前引擎都支持。

Firefox3.5+Safari4+Chrome3+
Opera12.1+Edge79+
Edge (旧版)12+Internet Explorer8+
Firefox Android?Safari iOS?Chrome Android?WebView Android37+Samsung Internet?Opera Android12.1+

返回当前选定的操作类型。如果操作类型不是effectAllowed属性允许的操作类型之一,则操作将失败。

可以设置,以更改选定的操作。

可能的值为“none”、“copy”、“link”和“move”。

dataTransfer.effectAllowed [ = value ]

DataTransfer/effectAllowed

所有当前引擎都支持。

Firefox3.5+Safari4+Chrome3+
Opera12.1+Edge79+
Edge (旧版)12+Internet Explorer8+
Firefox Android?Safari iOS?Chrome Android?WebView Android37+Samsung Internet?Opera Android12.1+

返回要允许的操作类型。

可以在(dragstart 事件期间)设置,以更改允许的操作。

可能的值为 "none"、"copy"、"copyLink"、"copyMove"、"link"、"linkMove"、"move"、"all" 和 "uninitialized"。

dataTransfer.items

DataTransfer/items

所有当前引擎都支持。

Firefox50+Safari11.1+Chrome3+
Opera12+Edge79+
Edge (旧版)12+Internet Explorer不支持
Firefox Android52+Safari iOS?Chrome Android?WebView Android37+Samsung Internet?Opera Android12+

返回一个 DataTransferItemList 对象,其中包含拖动数据。

dataTransfer.setDragImage(element, x, y)

DataTransfer/setDragImage

所有当前引擎都支持。

Firefox3.5+Safari4+Chrome3+
Opera12.1+Edge79+
Edge (旧版)18Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android37+Samsung Internet?Opera Android12.1+

使用给定的元素更新拖动反馈,替换任何先前指定的反馈。

dataTransfer.types

DataTransfer/types

所有当前引擎都支持。

Firefox3.5+Safari4+Chrome3+
Opera12.1+Edge79+
Edge (旧版)12+Internet Explorer10+
Firefox Android?Safari iOS?Chrome Android?WebView Android37+Samsung Internet?Opera Android12.1+

返回一个 冻结数组,其中列出了在 dragstart 事件中设置的格式。此外,如果正在拖动任何文件,则其中一个类型将是字符串 "Files"。

data = dataTransfer.getData(format)

DataTransfer/getData

所有当前引擎都支持。

Firefox3.5+Safari4+Chrome3+
Opera12.1+Edge79+
Edge (旧版)12+Internet Explorer8+
Firefox Android?Safari iOS?Chrome Android?WebView Android37+Samsung Internet?Opera Android12.1+

返回指定的数据。如果没有此类数据,则返回空字符串。

dataTransfer.setData(format, data)

DataTransfer/setData

所有当前引擎都支持。

Firefox3.5+Safari5+Chrome3+
Opera12+Edge79+
Edge (旧版)12+Internet Explorer8+
Firefox Android?Safari iOS5+Chrome Android?WebView Android37+Samsung Internet?Opera Android12+

添加指定的数据。

dataTransfer.clearData([ format ])

DataTransfer/clearData

所有当前引擎都支持。

Firefox3.5+Safari4+Chrome3+
Opera12.1+Edge79+
Edge (旧版)12+Internet Explorer8+
Firefox Android?Safari iOS?Chrome Android?WebView Android37+Samsung Internet?Opera Android12.1+

删除指定格式的数据。如果省略参数,则删除所有数据。

dataTransfer.files

DataTransfer/files

所有当前引擎都支持。

Firefox3.6+Safari4+Chrome3+
Opera12.1+Edge79+
Edge (旧版)12+Internet Explorer10+
Firefox Android?Safari iOS?Chrome Android?WebView Android37+Samsung Internet?Opera Android12.1+

返回一个 FileList,其中包含正在拖动的文件(如果有)。

作为 拖放事件的一部分创建的 DataTransfer 对象仅在这些事件触发时有效。

一个 DataTransfer 对象与其 拖动数据存储 关联,前提是它有效。

一个 DataTransfer 对象具有关联的 类型数组,它是一个 FrozenArray<DOMString>,最初为空。当 DataTransfer 对象的 拖动数据存储项列表 内容发生更改或 DataTransfer 对象不再与 拖动数据存储 关联时,运行以下步骤

  1. L 为一个空序列。

  2. 如果 DataTransfer 对象仍与 拖动数据存储 关联,则

    1. 对于 DataTransfer 对象的 拖动数据存储项列表 中每个 类型text 的项,在 L 中添加一个条目,该条目包含该项的 类型字符串

    2. 如果 DataTransfer 对象的 拖动数据存储项列表 中有任何 类型File 的项,则在 L 中添加一个条目,该条目包含字符串 "Files"。(此值可以与其他值区分开来,因为它不是小写字母。)

  3. DataTransfer 对象的 类型数组 设置为 L 创建冻结数组 的结果。

当调用 DataTransfer() 构造函数时,必须返回一个新创建的 DataTransfer 对象,其初始化如下

  1. 拖动数据存储项列表 设置为空列表。

  2. 拖动数据存储模式 设置为 读写模式

  3. dropEffecteffectAllowed 设置为 "none"。

dropEffect 属性控制用户在拖放操作期间获得的拖放反馈。当创建 DataTransfer 对象时,dropEffect 属性设置为字符串值。在获取时,它必须返回其当前值。在设置时,如果新值为 "none"、"copy"、"link" 或 "move" 之一,则属性的当前值必须设置为新值。其他值必须忽略。

effectAllowed 属性用于拖放处理模型中初始化 dropEffect 属性,在 dragenterdragover 事件期间。当创建 DataTransfer 对象时,effectAllowed 属性设置为字符串值。在获取时,它必须返回其当前值。在设置时,如果 拖动数据存储模式读写模式 且新值为 "none"、"copy"、"copyLink"、"copyMove"、"link"、"linkMove"、"move"、"all" 或 "uninitialized" 之一,则属性的当前值必须设置为新值。否则,它必须保持不变。

items 属性必须返回与 DataTransfer 对象关联的 DataTransferItemList 对象。

setDragImage(image, x, y) 方法必须运行以下步骤

  1. 如果 DataTransfer 对象不再与 拖动数据存储 关联,则返回。不会发生任何事情。

  2. 如果 拖动数据存储模式 不是 读写模式,则返回。不会发生任何事情。

  3. 如果 image 是一个 img 元素,则将 拖动数据存储位图 设置为该元素的图像(在其 自然大小 处);否则,将 拖动数据存储位图 设置为从给定元素生成的图像(执行此操作的确切机制目前未指定)。

  4. 拖动数据存储热点坐标 设置为给定的 xy 坐标。

types 属性必须返回此 DataTransfer 对象的 类型数组

getData(format) 方法必须运行以下步骤

  1. 如果 DataTransfer 对象不再与 拖动数据存储 关联,则返回空字符串。

  2. 如果 拖动数据存储模式受保护模式,则返回空字符串。

  3. format 为第一个参数,转换为 ASCII 小写字母

  4. convert-to-URL 为 false。

  5. 如果 format 等于 "text",则将其更改为 "text/plain"。

  6. 如果 format 等于 "url",则将其更改为 "text/uri-list" 并将 convert-to-URL 设置为 true。

  7. 如果在 拖放数据存储项列表 中没有 类型text类型字符串 等于 format 的项,则返回空字符串。

  8. result拖放数据存储项列表类型Plain Unicode string类型字符串 等于 format 的项的数据。

  9. 如果 convert-to-URL 为 true,则根据 text/uri-list 数据对 result 进行解析,然后将 result 设置为列表中的第一个 URL(如果有),否则设置为空字符串。 [RFC2483]

  10. 返回 result

setData(format, data) 方法必须执行以下步骤

  1. 如果 DataTransfer 对象不再与 拖放数据存储 关联,则返回。什么也不会发生。

  2. 如果 拖放数据存储模式 不是 读写模式,则返回。什么也不会发生。

  3. format 为第一个参数,转换为 ASCII 小写

  4. 如果 format 等于 "text",则将其更改为 "text/plain"。

    如果 format 等于 "url",则将其更改为 "text/uri-list"。

  5. 删除 拖放数据存储项列表类型text类型字符串 等于 format 的项(如果有)。

  6. 拖放数据存储项列表 添加一个 类型text类型字符串 等于 format 且数据为方法的第二个参数给定的字符串的项。

clearData(format) 方法必须执行以下步骤

  1. 如果 DataTransfer 对象不再与 拖放数据存储 关联,则返回。什么也不会发生。

  2. 如果 拖放数据存储模式 不是 读写模式,则返回。什么也不会发生。

  3. 如果该方法在没有参数的情况下被调用,则删除 拖放数据存储项列表 中每个 类型Plain Unicode string 的项,并返回。

  4. format 设置为 format转换为 ASCII 小写

  5. 如果 format 等于 "text",则将其更改为 "text/plain"。

    如果 format 等于 "url",则将其更改为 "text/uri-list"。

  6. 删除 拖放数据存储项列表类型text类型字符串 等于 format 的项(如果有)。

clearData() 方法不影响拖动过程中是否包含任何文件,因此 types 属性的列表在调用 clearData() 后可能仍然不为空(如果拖动过程中包含任何文件,它仍然包含 "Files" 字符串)。

files 属性必须返回一个 动态FileList 序列,该序列由表示通过以下步骤找到的文件的 File 对象组成。此外,对于给定的 FileList 对象和给定的底层文件,每次都必须使用相同的 File 对象。

  1. 以一个空列表 L 开始。

  2. 如果 DataTransfer 对象不再与 拖放数据存储 关联,则 FileList 为空。返回空列表 L

  3. 如果 拖放数据存储模式受保护模式,则返回空列表 L

  4. 对于 拖放数据存储项列表 中每个 类型File 的项,将该项的数据(特别是文件的文件名和内容,以及其 类型)添加到列表 L 中。

  5. 通过这些步骤找到的文件是列表 L 中的文件。

此版本的 API 在拖动过程中不公开文件的类型。

6.11.3.1 DataTransferItemList 接口

DataTransferItemList

所有当前引擎都支持。

Firefox50+Safari6+Chrome13+
Opera12+Edge79+
Edge (旧版)12+Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android14+

每个 DataTransfer 对象都与一个 DataTransferItemList 对象关联。

[Exposed=Window]
interface DataTransferItemList {
  readonly attribute unsigned long length;
  getter DataTransferItem (unsigned long index);
  DataTransferItem? add(DOMString data, DOMString type);
  DataTransferItem? add(File data);
  undefined remove(unsigned long index);
  undefined clear();
};
items.length

DataTransferItemList/length

所有当前引擎都支持。

Firefox50+Safari6+Chrome13+
Opera12+Edge79+
Edge (旧版)12+Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android14+

返回 拖放数据存储 中的项目数。

items[index]

返回表示 拖放数据存储 中第 index 个条目的 DataTransferItem 对象。

items.remove(index)

DataTransferItemList/remove

所有当前引擎都支持。

Firefox50+Safari6+Chrome31+
Opera12+Edge79+
Edge (旧版)12+Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android14+

删除 拖放数据存储 中的第 index 个条目。

items.clear()

DataTransferItemList/clear

所有当前引擎都支持。

Firefox50+Safari6+Chrome13+
Opera12+Edge79+
Edge (旧版)12+Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android14+

删除 拖放数据存储 中的所有条目。

items.add(data)

DataTransferItemList/add

所有当前引擎都支持。

Firefox50+Safari6+Chrome13+
Opera12+Edge79+
Edge (旧版)12+Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android14+
items.add(data, type)

为给定数据向 拖放数据存储 添加一个新条目。如果数据是纯文本,则还必须提供 type 字符串。

DataTransferItemList 对象的 DataTransfer 对象与 拖放数据存储 关联时,DataTransferItemList 对象的模式拖放数据存储模式 相同。当 DataTransferItemList 对象的 DataTransfer 对象拖放数据存储 关联时,DataTransferItemList 对象的模式禁用模式。本节中引用的 拖放数据存储(仅在 DataTransferItemList 对象不处于禁用模式时使用)是 DataTransferItemList 对象的 DataTransfer 对象关联的 拖放数据存储

如果对象处于禁用模式,则 length 属性必须返回零;否则,它必须返回 拖放数据存储项列表 中的项目数。

DataTransferItemList 对象不处于禁用模式时,其 支持的属性索引拖放数据存储项列表索引

确定 DataTransferItemList 对象的索引属性 i 的值,用户代理必须返回一个表示 拖放数据存储 中第 i 个项目的 DataTransferItem 对象。每次从该 DataTransferItemList 对象获取特定项目时,都必须返回相同的对象。当 DataTransferItem 对象首次创建时,它必须与与 DataTransferItemList 对象相同的 DataTransfer 对象关联。

add() 方法必须执行以下步骤

  1. 如果 DataTransferItemList 对象不处于 读写模式,则返回 null。

  2. 跳转到以下列表中的适当步骤集

    如果方法的第一个参数是字符串

    如果 拖放数据存储项列表 中已存在一个 类型text类型字符串 等于该方法第二个参数值(转换为 ASCII 小写)的项,则抛出一个 "NotSupportedError" DOMException

    否则,向 拖放数据存储项列表 添加一个项,该项的 类型text类型字符串 等于该方法第二个参数值(转换为 ASCII 小写),其数据为该方法第一个参数给出的字符串。

    如果该方法的第一个参数是 File

    拖放数据存储项列表 添加一个项,该项的 类型File类型字符串类型File转换为 ASCII 小写),其数据与 File 的数据相同。

  3. 确定与新添加的项相对应的索引属性的值,并返回该值(一个新创建的 DataTransferItem 对象)。

remove(index) 方法必须运行以下步骤

  1. 如果 DataTransferItemList 对象不在 读写模式 下,则抛出一个 "InvalidStateError" DOMException

  2. 如果 拖放数据存储 不包含第 index 个项,则返回。

  3. 拖放数据存储 中删除第 index 个项。

clear() 方法,如果 DataTransferItemList 对象处于 读写模式 下,则必须从 拖放数据存储 中删除所有项。否则,它必须什么也不做。

6.11.3.2 DataTransferItem 接口

DataTransferItem

所有当前引擎都支持。

Firefox50+Safari5.1+Chrome11+
Opera12+Edge79+
Edge (旧版)12+Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android4+Samsung Internet?Opera Android14+

每个 DataTransferItem 对象都与一个 DataTransfer 对象相关联。

[Exposed=Window]
interface DataTransferItem {
  readonly attribute DOMString kind;
  readonly attribute DOMString type;
  undefined getAsString(FunctionStringCallback? _callback);
  File? getAsFile();
};

callback FunctionStringCallback = undefined (DOMString data);
item.kind

DataTransferItem/kind

所有当前引擎都支持。

Firefox50+Safari5.1+Chrome11+
Opera12+Edge79+
Edge (旧版)12+Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android4+Samsung Internet?Opera Android14+

返回 拖放数据项类型,其中之一:"string"、"file"。

item.type

DataTransferItem/type

所有当前引擎都支持。

Firefox50+Safari5.1+Chrome11+
Opera12+Edge79+
Edge (旧版)12+Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android4+Samsung Internet?Opera Android14+

返回 拖放数据项类型字符串

item.getAsString(callback)

DataTransferItem/getAsString

所有当前引擎都支持。

Firefox50+Safari5.1+Chrome11+
Opera12+Edge79+
Edge (旧版)12+Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android4+Samsung Internet?Opera Android14+

如果 拖放数据项类型text,则使用字符串数据作为参数调用回调函数。

file = item.getAsFile()

DataTransferItem/getAsFile

所有当前引擎都支持。

Firefox50+Safari5.1+Chrome11+
Opera12+Edge79+
Edge (旧版)12+Internet Explorer不支持
Firefox Android?Safari iOS?Chrome Android?WebView Android4+Samsung Internet?Opera Android14+

如果 拖放数据项类型File,则返回一个 File 对象。

DataTransferItem 对象的 DataTransfer 对象与 拖放数据存储 相关联,并且该 拖放数据存储拖放数据存储项列表 仍然包含 DataTransferItem 对象表示的项时,DataTransferItem 对象的 模式拖放数据存储模式 相同。当 DataTransferItem 对象的 DataTransfer 对象 *不* 与 拖放数据存储 相关联,或者如果 DataTransferItem 对象表示的项已从相关的 拖放数据存储项列表 中删除,则 DataTransferItem 对象的 模式禁用模式。本节中引用的 拖放数据存储(仅在 DataTransferItem 对象不处于 禁用模式 下时使用)是与 DataTransferItem 对象的 DataTransfer 对象相关联的 拖放数据存储

kind 属性必须返回空字符串,如果 DataTransferItem 对象处于 禁用模式 下;否则,它必须返回下表中第二列中给出的字符串,该字符串来自第一列中包含 DataTransferItem 对象表示的项的 拖放数据项类型 的行的单元格。

类型字符串
文本 "string"
文件 "file"

type 属性必须返回空字符串,如果 DataTransferItem 对象处于 禁用模式 下;否则,它必须返回 DataTransferItem 对象表示的项的 拖放数据项类型字符串

getAsString(callback) 方法必须运行以下步骤

  1. 如果 callback 为 null,则返回。

  2. 如果 DataTransferItem 对象不处于 读写模式只读模式 下,则返回。回调函数永远不会被调用。

  3. 如果 拖放数据项类型 不是 text,则返回。回调函数永远不会被调用。

  4. 否则,将一个任务排队 以调用 callback,并将 DataTransferItem 对象表示的项的实际数据作为参数传递。

getAsFile() 方法必须运行以下步骤

  1. 如果 DataTransferItem 对象不处于 读写模式只读模式 下,则返回 null。

  2. 如果 拖放数据项类型 不是 File,则返回 null。

  3. 返回一个新的 File 对象,表示 DataTransferItem 对象表示的项的实际数据。

6.11.4 DragEvent 接口

DragEvent/DragEvent

所有当前引擎都支持。

Firefox3.5+Safari14+Chrome46+
Opera12+Edge79+
Edge (旧版)12+Internet Explorer不支持
Firefox Android?Safari iOS不支持Chrome Android不支持WebView Android?Samsung Internet?Opera Android?

DragEvent

所有当前引擎都支持。

Firefox3.5+Safari14+Chrome46+
Opera12+Edge79+
Edge (Legacy)12+Internet Explorer9+
Firefox Android?Safari iOS不支持Chrome Android不支持WebView Android?Samsung Internet?Opera Android?

拖放处理模型涉及多个事件。它们都使用 DragEvent 接口。

[Exposed=Window]
interface DragEvent : MouseEvent {
  constructor(DOMString type, optional DragEventInit eventInitDict = {});

  readonly attribute DataTransfer? dataTransfer;
};

dictionary DragEventInit : MouseEventInit {
  DataTransfer? dataTransfer = null;
};
event.dataTransfer

DragEvent/dataTransfer

所有当前引擎都支持。

Firefox3.5+Safari14+Chrome46+
Opera?Edge79+
Edge (Legacy)12+Internet Explorer🔰 10+
Firefox Android?Safari iOS不支持Chrome Android不支持WebView Android?Samsung Internet?Opera Android?

返回事件的 DataTransfer 对象。

尽管为了与其他事件接口保持一致,DragEvent 接口有一个构造函数,但它并不是特别有用。特别是,无法从脚本创建有用的 DataTransfer 对象,因为 DataTransfer 对象具有由浏览器在拖放过程中协调的处理和安全模型。

DragEvent 接口的 dataTransfer 属性必须返回其初始化的值。它表示事件的上下文信息。

当用户代理需要在元素上 触发名为 e 的 DND 事件 时,使用特定的 拖放数据存储,并可选地使用特定的 相关目标,用户代理必须运行以下步骤

  1. dataDragStoreWasChanged 为 false。
  2. 如果没有提供特定的 相关目标,则将 相关目标 设置为 null。

  3. window 为指定目标元素的 Document 对象的 相关全局对象

  4. 如果 edragstart,则将 拖放数据存储模式 设置为 读写模式,并将 dataDragStoreWasChanged 设置为 true。

    如果 edrop,则将 拖放数据存储模式 设置为 只读模式

  5. dataTransfer 为一个新创建的与给定 拖放数据存储 关联的 DataTransfer 对象。

  6. effectAllowed 属性设置为 拖放数据存储拖放数据存储允许的效果状态

  7. 如果 edragstartdragdragleave,则将 dropEffect 属性设置为 “none”;如果 edropdragend,则将其设置为与 当前拖放操作 对应的值;否则(即,如果 edragenterdragover),则将其设置为基于 effectAllowed 属性的值和拖放源的值,如下表所示。

    effectAlloweddropEffect
    "none""none"
    "copy""copy"
    "copyLink""copy",或者,如果合适,则为 “link
    "copyMove""copy",或者,如果合适,则为 “move
    "all""copy",或者,如果合适,则为 “link” 或 “move
    "link""link"
    "linkMove""link",或者,如果合适,则为 “move
    "move""move"
    当拖动的是文本控件中的选择内容时,uninitialized"move",或者,如果合适,则为 “copy” 或 “link
    当拖动的是选择内容时,uninitialized"copy",或者,如果合适,则为 “link” 或 “move
    当拖动的是具有 href 属性的 a 元素时,uninitialized"link",或者,如果合适,则为 “copy” 或 “move
    任何其他情况"copy",或者,如果合适,则为 “link” 或 “move

    如果上表提供了 可能合适的替代方案,则用户代理可以在平台约定指示用户已请求这些替代效果的情况下,使用列出的替代值。

    例如,Windows 平台约定规定,按住“Alt”键拖动表示用户希望链接数据,而不是移动或复制数据。因此,在 Windows 系统上,如果上表中“link” 是一个选项,并且同时按下“Alt”键,则用户代理可以选择该选项,而不是 “copy” 或 “move”。

  8. event 为使用 DragEvent 创建事件 的结果。

  9. eventtype 属性初始化为 e,其 bubbles 属性初始化为 true,其 view 属性初始化为 window,其 relatedTarget 属性初始化为 相关目标,其 dataTransfer 属性初始化为 dataTransfer

  10. 如果 e 不是 dragleavedragend,则将 eventcancelable 属性初始化为 true。

  11. 根据输入设备的状态(如同用户交互事件一样)初始化 event 的鼠标和键盘属性。

    如果没有相关的指向设备,则将 eventscreenXscreenYclientXclientYbutton 属性初始化为 0。

  12. 在指定的目标元素上 分派 event

  13. 拖放数据存储允许的效果状态 设置为 dataTransfereffectAllowed 属性的当前值。(只有当 edragstart 时,它才能更改值。)

  14. 如果 dataDragStoreWasChanged 为 true,则将 拖放数据存储模式 恢复为 受保护模式

  15. 断开 dataTransfer拖放数据存储 之间的关联。

6.11.5 处理模型

当用户尝试开始拖放操作时,用户代理必须运行以下步骤。即使拖放实际上是在另一个文档或应用程序中开始的,并且用户代理直到它与用户代理管辖范围内的文档相交时才意识到拖放正在发生,用户代理也必须像运行这些步骤一样进行操作。

  1. 确定正在拖动的内容,如下所示

    如果拖放操作是在选择内容上调用的,则正在拖动的是选择内容。

    否则,如果拖放操作是在 Document 上调用的,则它是从用户尝试拖动的节点开始,向上遍历祖先链的第一个具有 IDL 属性 draggable 设置为 true 的元素。如果没有这样的元素,则没有正在拖动的内容;返回,拖放操作从未开始。

    否则,拖放操作是在用户代理的管辖范围之外调用的。正在拖动的内容由拖放开始的文档或应用程序定义。

    img 元素和具有 href 属性的 a 元素默认情况下将其 draggable 属性设置为 true。

  2. 创建拖放数据存储。本节中步骤随后触发的所有 DND 事件都必须使用此 拖放数据存储

  3. 确定哪个 DOM 节点是 源节点,如下所示

    如果拖动的是选区,则源节点是用户开始拖动操作的文本节点(通常是用户最初点击的文本节点)。如果用户没有指定特定的节点,例如,如果用户只是告诉用户代理开始拖动“选区”,则源节点是包含选区一部分的第一个文本节点。

    否则,如果拖动的是元素,则源节点是被拖动的元素。

    否则,源节点是另一个文档或应用程序的一部分。当本规范要求在此情况下在源节点上分发事件时,用户代理必须遵循与该情况相关的平台特定约定。

    在拖放操作过程中,会在源节点上触发多个事件。

  4. 确定被拖动节点列表,如下所示

    如果拖动的是选区,则被拖动节点列表树序包含选区中部分或完全包含的每个节点(包括它们的所有祖先)。

    否则,被拖动节点列表仅包含源节点(如果有)。

  5. 如果拖动的是选区,则向拖放数据存储项列表添加一个项,其属性设置如下

    拖放数据项类型字符串
    "text/plain"
    拖放数据项类型
    文本
    实际数据
    选区的文本

    否则,如果拖动任何文件,则为每个文件向拖放数据存储项列表添加一个项,其属性设置如下

    拖放数据项类型字符串
    文件的 MIME 类型(如果已知),否则为"application/octet-stream"。
    拖放数据项类型
    文件
    实际数据
    文件的内容和名称。

    目前,拖动文件只能从可导航区域外部进行,例如从文件系统管理器应用程序进行。

    如果拖动操作从应用程序外部发起,则用户代理必须根据被拖动数据的类型向拖放数据存储项列表添加项,并在适当情况下遵循平台约定;但是,如果平台约定不使用MIME 类型来标记被拖动的数据,则用户代理必须尽力尝试将类型映射到 MIME 类型,并且在任何情况下,所有拖放数据项类型字符串都必须转换为 ASCII 小写

    用户代理还可以添加一个或多个表示选区或被拖动元素的其他形式的项,例如 HTML。

  6. 如果被拖动节点列表不为空,则将这些节点的微数据提取为 JSON 格式,并向拖放数据存储项列表添加一个项,其属性设置如下

    拖放数据项类型字符串
    application/microdata+json
    拖放数据项类型
    文本
    实际数据
    生成的 JSON 字符串。
  7. 运行以下子步骤

    1. urls为« »。

    2. 对于被拖动节点列表中的每个node

      如果节点是具有href属性的a元素
      将根据给定元素的href内容属性的值(相对于元素的节点文档编码、解析和序列化 URL的结果添加到urls中。
      如果节点是具有src属性的img元素
      将根据给定元素的src内容属性的值(相对于元素的节点文档编码、解析和序列化 URL的结果添加到urls中。
    3. 如果urls仍然为空,则返回。

    4. url 字符串为将urls中的字符串按添加顺序连接起来的结果,并用 U+000D 回车 U+000A 换行字符对 (CRLF) 分隔。

    5. 拖放数据存储项列表添加一个项,其属性设置如下

      拖放数据项类型字符串
      text/uri-list
      拖放数据项类型
      文本
      实际数据
      url 字符串
  8. 根据用户代理更新拖放数据存储默认反馈(如果用户正在拖动选区,则选区可能是此反馈的基础;如果用户正在拖动元素,则将使用该元素的渲染;如果拖动从用户代理外部开始,则应使用确定拖动反馈的平台约定)。

  9. 源节点触发一个名为dragstart的 DND 事件。

    如果事件被取消,则不应发生拖放操作;返回。

    由于几乎可以肯定没有注册事件监听器的事件永远不会被取消,因此如果作者没有明确阻止,拖放始终可供用户使用。

  10. 源节点触发一个名为pointercancel的指针事件,并根据指针事件的要求触发任何其他后续事件。[POINTEREVENTS]

  11. 以与平台约定一致的方式,并如下所述启动拖放操作

    拖放反馈必须从以下第一个可用的来源生成

    1. 如果有拖放数据存储位图。在这种情况下,应使用拖放数据存储热点坐标作为提示,指示相对于生成的图像放置光标的位置。这些值表示以CSS 像素为单位的距离,分别从图像的左侧和顶部开始。[CSS]
    2. 拖放数据存储默认反馈

从用户代理需要启动拖放操作的那一刻起,到拖放操作结束时,必须抑制设备输入事件(例如鼠标和键盘事件)。

在拖动操作期间,用户直接指示为放置目标的元素称为直接用户选择。(只有元素才能被用户选择;其他节点不得作为放置目标提供。)但是,直接用户选择不一定是当前目标元素,后者是当前为拖放操作的放置部分选择的元素。

当用户选择不同的元素时(通过指向它们或以其他方式选择它们),直接用户选择会发生变化。根据文档中事件监听器的结果,当前目标元素会在直接用户选择发生变化时发生变化,如下所述。

当前目标元素直接用户选择都可以为 null,这意味着没有选择目标元素。它们也可以都是其他(基于 DOM 的)文档或其他(非 Web)程序中的元素。(例如,用户可以将文本拖动到文字处理器中。)当前目标元素最初为 null。

此外,还有一个当前拖动操作,它可以取值“none”、“copy”、“link”和“move”。最初,它的值为“none”。它会根据以下步骤中的描述由用户代理更新。

用户代理必须在拖动操作启动后以及此后只要拖动操作正在进行,每 350 毫秒 (±200 毫秒) 就排队执行一个任务,以按顺序执行以下步骤

  1. 如果当下一个迭代到期时,用户代理仍在执行序列的先前迭代(如果有),则对于此迭代返回(有效地“跳过拖放操作的错过的帧”)。

  2. 源节点触发一个名为drag的 DND 事件。如果此事件被取消,则用户代理必须将当前拖动操作设置为“none”(没有拖动操作)。

  3. 如果drag事件未被取消且用户未结束拖放操作,则检查拖放操作的状态,如下所示

    1. 如果用户的当前选中项与上一次迭代期间的选中项不同(或者如果这是第一次迭代),并且当前选中项与当前目标元素不同,则按如下方式更新当前目标元素。

      如果新的当前选中项为空

      也将当前目标元素设置为 null。

      如果新的当前选中项位于非 DOM 文档或应用程序中

      将当前目标元素设置为当前选中项。

      否则

      在当前选中项上触发名为 dragenter 的 DND 事件。

      如果事件被取消,则将当前目标元素设置为当前选中项。

      否则,从以下列表中运行相应的步骤

      如果当前选中项是文本控件(例如,textarea,或 input 元素,其 type 属性处于 文本 状态)或编辑宿主或可编辑元素,并且拖动数据存储项列表中有一个项目,其拖动数据项类型字符串为 "text/plain" 且拖动数据项类型为 text

      无论如何,将当前目标元素设置为当前选中项。

      如果当前选中项是 body 元素

      保持当前目标元素不变。

      否则

      如果存在 body 元素,则在 body 元素上触发名为 dragenter 的 DND 事件;如果不存在,则在 Document 对象上触发。然后,无论该事件是否被取消,都将当前目标元素设置为 body 元素

    2. 如果上一步导致当前目标元素发生更改,并且上一个目标元素不是 null 或非 DOM 文档的一部分,则在先前目标元素上触发名为 dragleave 的 DND 事件,并将新的当前目标元素作为特定的 相关目标

    3. 如果当前目标元素是 DOM 元素,则在此当前目标元素上触发名为 dragover 的 DND 事件。

      如果 dragover 事件未被取消,则从以下列表中运行相应的步骤

      如果当前目标元素是文本控件(例如,textarea,或 input 元素,其 type 属性处于 文本 状态)或编辑宿主或可编辑元素,并且拖动数据存储项列表中有一个项目,其拖动数据项类型字符串为 "text/plain" 且拖动数据项类型为 text

      根据平台约定,将当前拖动操作设置为 "copy" 或 "move"。

      否则

      将当前拖动操作重置为 "none"。

      否则(如果 dragover 事件取消),则根据 effectAlloweddropEffect 属性的值设置当前拖动操作,这些属性属于 DragEvent 对象的 dataTransfer 对象,其状态是在事件 分派 完成后,如下表所示

      effectAlloweddropEffect拖动操作
      "uninitialized","copy","copyLink","copyMove" 或 "all""copy""copy"
      "uninitialized","link","copyLink","linkMove" 或 "all""link""link"
      "uninitialized","move","copyMove","linkMove" 或 "all""move""move"
      任何其他情况"none"
    4. 否则,如果当前目标元素不是 DOM 元素,则使用特定于平台的机制来确定正在执行的拖动操作(无、复制、链接或移动),并相应地设置当前拖动操作。

    5. 更新拖动反馈(例如鼠标光标),以匹配当前拖动操作,如下所示

      拖动操作反馈
      "copy"如果在此处放下,则数据将被复制。
      "link"如果在此处放下,则数据将被链接。
      "move"如果在此处放下,则数据将被移动。
      "none"不允许任何操作,在此处放下将取消拖放操作。
  4. 否则,如果用户结束了拖放操作(例如,在鼠标驱动的拖放界面中释放鼠标按钮),或者 drag 事件被取消,则这将是最后一次迭代。运行以下步骤,然后停止拖放操作

    1. 如果当前拖动操作为 "none"(无拖动操作),或者如果用户通过取消操作结束了拖放操作(例如,按 Escape 键),或者如果当前目标元素为 null,则拖动操作失败。运行这些子步骤

      1. dropped 为 false。

      2. 如果当前目标元素是 DOM 元素,则在该元素上触发名为 dragleave 的 DND 事件;否则,如果它不是 null,则使用特定于平台的约定来取消拖动。

      3. 将当前拖动操作设置为 "none"。

      否则,拖动操作可能成功;运行这些子步骤

      1. dropped 为 true。

      2. 如果当前目标元素是 DOM 元素,则在该元素上触发名为 drop 的 DND 事件;否则,使用特定于平台的约定来指示放下。

      3. 如果事件被取消,则将当前拖动操作设置为dropEffect属性的值,该属性属于DragEvent对象的dataTransfer对象,其状态为事件分发完成后所处的状态。

        否则,事件未被取消;执行事件的默认操作,具体取决于确切的目标,如下所示

        如果当前目标元素是文本控件(例如,textarea,或input元素,其type属性处于文本状态)或编辑宿主可编辑元素,并且拖动数据存储项列表中有一个项目,其拖动数据项类型字符串为"text/plain",并且拖动数据项类型text

        拖动数据存储项列表中第一个项目的实际数据插入到文本控件或编辑宿主可编辑元素中,使其拖动数据项类型字符串为"text/plain",并且拖动数据项类型text,方式应与平台特定的约定一致(例如,将其插入到当前鼠标光标位置,或将其插入到字段末尾)。

        否则

        当前拖动操作重置为"none"。

    2. 源节点触发一个名为dragend的DND事件。

    3. 作为dragend事件的默认操作,运行以下列表中的相应步骤

      如果dropped为true,当前目标元素文本控件(见下文),当前拖动操作为"move",并且拖放操作的源是DOM中的一个选择,该选择完全包含在编辑宿主

      删除选择。.

      如果dropped为true,当前目标元素文本控件(见下文),当前拖动操作为"move",并且拖放操作的源是文本控件中的一个选择

      用户代理应从相关的文本控件中删除拖动的选择。

      如果dropped为false,或者当前拖动操作为"none"

      拖动被取消。如果平台约定规定需要向用户显示此操作(例如,通过动画显示拖动的选择回到拖放操作的源),则执行此操作。

      否则

      该事件没有默认操作。

      在本步骤中,文本控件是指textarea元素或input元素,其type属性处于文本搜索电话URL电子邮件密码数字状态之一。

鼓励用户代理考虑如何在可滚动区域边缘附近对拖动做出反应。例如,如果用户将链接拖到长页面上视口的底部,则可能需要滚动页面,以便用户可以将链接放到页面上的更下方。

此模型独立于所涉及节点来自哪个Document对象;事件按上述方式触发,并且处理模型的其余部分按上述方式运行,无论操作涉及多少个文档。

6.11.6 事件摘要

本节是非规范性的。

以下事件参与拖放模型。

事件名称目标可取消? 拖动数据存储模式 dropEffect 默认操作
dragstart

HTMLElement/dragstart_event

所有当前引擎都支持。

Firefox9+Safari3.1+Chrome1+
Opera12+Edge79+
Edge (Legacy)12+Internet Explorer9+
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android12+
源节点✓ 可取消读写模式 "none"启动拖放操作
drag

HTMLElement/drag_event

所有当前引擎都支持。

Firefox9+Safari3.1+Chrome1+
Opera12+Edge79+
Edge (Legacy)12+Internet Explorer9+
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android12+
源节点✓ 可取消保护模式 "none"继续拖放操作
dragenter

HTMLElement/dragenter_event

所有当前引擎都支持。

Firefox9+Safari3.1+Chrome1+
Opera12+Edge79+
Edge (Legacy)12+Internet Explorer9+
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android12+
立即用户选择body元素✓ 可取消保护模式 基于effectAllowed拒绝立即用户选择作为潜在的目标元素
dragleave

HTMLElement/dragleave_event

所有当前引擎都支持。

Firefox9+Safari3.1+Chrome1+
Opera12+Edge79+
Edge (Legacy)12+Internet Explorer9+
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android12+
上一个目标元素保护模式 "none"
dragover

HTMLElement/dragover_event

所有当前引擎都支持。

Firefox9+Safari3.1+Chrome1+
Opera12+Edge79+
Edge (Legacy)12+Internet Explorer9+
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android12+
当前目标元素✓ 可取消保护模式 基于effectAllowed当前拖动操作重置为“none”
drop

HTMLElement/drop_event

所有当前引擎都支持。

Firefox9+Safari3.1+Chrome1+
Opera12+Edge79+
Edge (Legacy)12+Internet Explorer9+
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android12+
当前目标元素✓ 可取消只读模式 当前拖动操作变化
dragend

HTMLElement/dragend_event

所有当前引擎都支持。

Firefox9+Safari3.1+Chrome1+
Opera12+Edge79+
Edge (Legacy)12+Internet Explorer9+
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android12+
源节点保护模式 当前拖动操作变化

所有这些事件都冒泡并组成,并且effectAllowed属性始终具有dragstart事件之后的值,在dragstart事件中默认为"uninitialized"。

6.11.7 draggable属性

Global_attributes/draggable

所有当前引擎都支持。

Firefox2+Safari5+Chrome4+
Opera12+Edge79+
Edge (Legacy)12+Internet ExplorerYes
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

所有HTML元素都可以设置draggable内容属性。draggable属性是一个枚举属性,具有以下关键字和状态

关键字状态简要说明
true true 该元素可拖动。
false false 该元素不可拖动。

属性的缺失值默认值无效值默认值均为auto状态。auto状态使用用户代理的默认行为。

具有draggable属性的元素也应具有title属性,用于非视觉交互的目的,为该元素命名。

element.draggable [ = value ]

如果元素可拖动,则返回true;否则,返回false。

可以设置,以覆盖默认值并设置draggable内容属性。

draggable IDL属性的值取决于内容属性的方式(如下所述),它控制元素是否可拖动。通常,只有文本选择可拖动,但其draggable IDL属性为true的元素也会变得可拖动。

如果元素的draggable内容属性处于true状态,则draggable IDL属性必须返回true。

否则,如果元素的draggable内容属性处于false状态,则draggable IDL属性必须返回false。

否则,元素的draggable内容属性处于auto状态。如果元素是img元素、object元素(表示图像)或a元素(具有href内容属性),则draggable IDL属性必须返回true;否则,draggable IDL属性必须返回false。

如果 draggable IDL 属性设置为 false,则 draggable 内容属性必须设置为字面值 "false"。如果 draggable IDL 属性设置为 true,则 draggable 内容属性必须设置为字面值 "true"。

6.11.8 拖放模型中的安全风险

用户代理在 dragstart 事件期间添加到 DataTransfer 对象中的数据,在 drop 事件发生之前,不得使其对脚本可用。否则,如果用户将敏感信息从一个文档拖放到第二个文档,并在过程中经过一个恶意第三方文档,则恶意文档可能会拦截这些数据。

出于同样的原因,用户代理必须仅在用户明确结束拖动操作时才认为拖放操作成功——如果任何脚本结束拖动操作,则必须将其视为不成功(取消),并且 drop 事件不得触发。

用户代理应注意不要响应脚本操作开始拖放操作。例如,在鼠标和窗口环境中,如果脚本在用户按下鼠标按钮时移动窗口,则用户代理不会将其视为开始拖动。这一点很重要,因为否则用户代理可能会在未经用户同意的情况下,导致数据从敏感源拖动并放到恶意文档中。

用户代理在拖动和放置内容时,应使用已知安全功能的安全列表过滤潜在的活动(脚本)内容(例如 HTML)。类似地,相对 URL 应转换为绝对 URL,以避免引用以意外方式更改。本规范未指定如何执行此操作。

假设一个恶意页面提供一些内容,并让用户选择并拖放(或者实际上是复制粘贴)这些内容到受害者页面的 contenteditable 区域。如果浏览器不确保仅拖放安全内容,则选择中的潜在不安全内容(如脚本和事件处理程序)一旦拖放到(或粘贴到)受害者站点,就会获得受害者站点的权限。这将使跨站点脚本攻击成为可能。