HTML5 拖放 API

此博文介绍了拖放功能的基础知识。

创建可拖动内容

在大多数浏览器中,默认情况下,所选文本、图片和链接都是可拖动的。 例如,如果您拖动网页上的链接,就会看到一个带有 可拖放到地址栏或桌面上, 或转到相应链接。要使其他类型的内容可拖动,您需要 需要使用 HTML5 拖放 API。

如需使对象可拖动,请在相应元素上设置 draggable=true。大约 包括图片、文件、链接、文件等 标记。

以下示例创建了一个接口,用于重新排列 使用 CSS 网格进行布局这些列的基本标记如下所示: 设置为 true 的每列的 draggable 属性:

<div class="container">
  <div draggable="true" class="box">A</div>
  <div draggable="true" class="box">B</div>
  <div draggable="true" class="box">C</div>
</div>

以下是 container 和 box 元素的 CSS。是唯一与 拖动功能是 cursor: move 属性。其余代码用于控制容器的布局和样式 和 box 元素的组合。

.container {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 10px;
}

.box {
  border: 3px solid #666;
  background-color: #ddd;
  border-radius: .5em;
  padding: 10px;
  cursor: move;
}

此时,您可以拖动这些项,但没有任何反应。添加 需要使用 JavaScript API。

监听拖动事件

如需监控拖动过程,您可以监听以下任意事件:

要处理拖动流程,您需要某种源元素(拖动 数据有效负载(正在拖动的事物)和目标( 抓住机会)。源元素几乎可以是任何类型的元素。通过 target 是接受用户数据的放置区域或放置区域集合 并非所有元素都可以作为目标。例如,您的定位条件不能 是图像。

开始和结束拖动序列

在内容上定义 draggable="true" 属性后,将 dragstart 事件处理脚本,开始执行每列的拖动序列。

此代码会在用户开始拖动时将列的不透明度设为 40%, 然后在拖动事件结束时将它恢复为 100%

function handleDragStart(e) {
  this.style.opacity = '0.4';
}

function handleDragEnd(e) {
  this.style.opacity = '1';
}

let items = document.querySelectorAll('.container .box');
items.forEach(function (item) {
  item.addEventListener('dragstart', handleDragStart);
  item.addEventListener('dragend', handleDragEnd);
});

结果可在以下 Glitch 演示中查看。拖动某个项及其 不透明度更改。由于源元素具有 dragstart 事件,因此将 this.style.opacity 到 40% 会为用户提供视觉反馈, 要移动的当前所选内容。放下项后,源元素 即使您尚未定义放下行为,也会恢复为 100% 的不透明度。

添加其他视觉提示

为帮助用户了解如何与您的界面进行交互,请使用 dragenterdragoverdragleave 事件处理脚本。在此示例中, 列是可拖动的放置目标。帮助用户: 为了理解这一点,当他们将拖动项拖到 列。例如,在 CSS 中,您可以为over 作为拖放目标的元素:

.box.over {
  border: 3px dotted #666;
}

然后,在 JavaScript 中设置事件处理脚本,在发生以下情况时添加 over 类: 该列,并在拖动的元素离开时将其移除。在 dragend 处理程序时,我们还要确保移除 拖动。

document.addEventListener('DOMContentLoaded', (event) => {

  function handleDragStart(e) {
    this.style.opacity = '0.4';
  }

  function handleDragEnd(e) {
    this.style.opacity = '1';

    items.forEach(function (item) {
      item.classList.remove('over');
    });
  }

  function handleDragOver(e) {
    e.preventDefault();
    return false;
  }

  function handleDragEnter(e) {
    this.classList.add('over');
  }

  function handleDragLeave(e) {
    this.classList.remove('over');
  }

  let items = document.querySelectorAll('.container .box');
  items.forEach(function(item) {
    item.addEventListener('dragstart', handleDragStart);
    item.addEventListener('dragover', handleDragOver);
    item.addEventListener('dragenter', handleDragEnter);
    item.addEventListener('dragleave', handleDragLeave);
    item.addEventListener('dragend', handleDragEnd);
    item.addEventListener('drop', handleDrop);
  });
});

此代码有几点值得讨论:

  • 默认操作dataTransfer.dropEffect 属性设置为dragover "none"。本页面的后面部分会介绍 dropEffect 属性。目前, 只需知道这会阻止 drop 事件触发。要覆盖 行为,请调用 e.preventDefault()。另一个好的做法是 false

  • dragenter 事件处理脚本用于切换 over 类,而不是 dragover。如果您使用 dragover,该事件会在用户 用于将拖动项放到列上方,使 CSS 类切换 。这会使浏览器进行大量不必要的渲染工作, 而这可能会影响用户体验我们强烈建议您 重新绘制,如果您需要使用 dragover,请考虑 限制事件监听器或使事件监听器去除抖动

完成降落

要处理放下事件,请为 drop 事件添加事件监听器。在drop中 处理程序时,您需要阻止浏览器的默认行为(即放下行为), 通常是某种令人厌烦的重定向。为此,请调用 e.stopPropagation()

function handleDrop(e) {
  e.stopPropagation(); // stops the browser from redirecting.
  return false;
}

请务必与其他处理程序一起注册新的处理程序:

  let items = document.querySelectorAll('.container .box');
  items.forEach(function(item) {
    item.addEventListener('dragstart', handleDragStart);
    item.addEventListener('dragover', handleDragOver);
    item.addEventListener('dragenter', handleDragEnter);
    item.addEventListener('dragleave', handleDragLeave);
    item.addEventListener('dragend', handleDragEnd);
    item.addEventListener('drop', handleDrop);
  });

如果此时运行代码,项不会放置到新位置。接收者 请使用 DataTransfer 对象。

dataTransfer 属性用于存放在拖动操作中发送的数据。dataTransferdragstart 事件中设置,并在拖放事件中读取或处理。正在呼叫 e.dataTransfer.setData(mimeType, dataPayload) 可用于设置对象的 MIME 类型和数据载荷。

在此示例中,我们将让用户重新排列列的顺序。 为此,您首先需要在拖动元素时存储源元素的 HTML。 开始时间:

function handleDragStart(e) {
  this.style.opacity = '0.4';

  dragSrcEl = this;

  e.dataTransfer.effectAllowed = 'move';
  e.dataTransfer.setData('text/html', this.innerHTML);
}

drop 事件中,您可以通过设置源列的 将 HTML 附加到您在其中放置数据的目标列的 HTML。这个 包括检查用户不会回到他们查看的同一列 。

function handleDrop(e) {
  e.stopPropagation();

  if (dragSrcEl !== this) {
    dragSrcEl.innerHTML = this.innerHTML;
    this.innerHTML = e.dataTransfer.getData('text/html');
  }

  return false;
}

您可以在以下演示中查看结果。为此,您需要 桌面浏览器。移动设备不支持 Drag and Drop API。拖动并 在 B 列的顶部释放 A 列,然后注意它们如何更改位置:

更多拖动属性

dataTransfer 对象公开了一些属性,用于向 并控制每个放下目标如何响应 特定数据类型。

  • dataTransfer.effectAllowed 用于限制“拖动类型”以及用户可以对元素执行哪些操作二手 在拖放处理模型中初始化 dropEffect dragenterdragover 事件。该属性可以有 以下值:nonecopycopyLinkcopyMovelinklinkMovemovealluninitialized
  • dataTransfer.dropEffect 控制用户在 dragenterdragover 期间获得的反馈 事件。当用户将指针悬停在目标元素上时,浏览器的 游标表示将要执行的操作类型,例如复制 或者移动。效果可采用以下值之一:nonecopylinkmove
  • e.dataTransfer.setDragImage(imgElement, x, y) 这意味着您不用使用浏览器的默认“重像”反馈,您 可以设置拖动图标。

上传文件

这个简单示例使用一列作为拖动源和拖动目标。这个 请求用户重新排列各项的界面中可能会出现的下滑在某些情况下 拖动目标和来源可能是不同的元素类型,例如在界面中 在该示例中,用户需要选择一张图片作为产品的主图片 将所选图片拖动到目标上。

拖放功能常用于让用户能够将项目从桌面拖入 应用主要区别在于 drop 处理程序。不使用 dataTransfer.getData() 来访问文件,其数据会包含在 dataTransfer.files 属性:

function handleDrop(e) {
  e.stopPropagation(); // Stops some browsers from redirecting.
  e.preventDefault();

  var files = e.dataTransfer.files;
  for (var i = 0, f; (f = files[i]); i++) {
    // Read the File objects in this FileList.
  }
}

您可以在 自定义拖放

更多资源