一次拖放多个HTML元素

26

HTML5是否有默认的方法可以将多个元素拖放到同一页面上的另一个html元素中?

似乎HTML5属性draggable只适用于一个元素,它说:“你可以拖动我,但你需要多个输入设备来选择同时拖动的其他可拖动元素。”

作为解决方法,可以使用js为所选元素添加标识符,并在特定事件中获取所有选定的元素并执行所需操作。但这真的是“正确”的方式吗?

当检查drop事件时,在处理文件拖放时,会出现多个文件的接口。至少在Chrome中还有items属性。

不同浏览器之间存在差异。

在Chrome中

drop事件包含items属性。

dataTransfer: DataTransfer
    items: DataTransferItemList
        length: 0

Items 似乎始终为0,无论您是否拖动了项目。

在Firefox中

drop 事件包含 mozItemCount 属性。

dataTransfer: DataTransfer
    mozItemCount: 1

mozItemCount 似乎至少为1。


这里有一个小演示

你可以在开发者工具控制台中观察:

此源代码取自此处:http://www.html5rocks.com/en/tutorials/dnd/basics/

$(function(){
  // Copied from: http://www.html5rocks.com/en/tutorials/dnd/basics/
  var cols_ = document.querySelectorAll('.column');
  var dragSrcEl_ = null;

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

    dragSrcEl_ = this;

    this.style.opacity = '0.4';

    // this/e.target is the source node.
    $(this).addClass('moving');
  };

  handleDragOver = function(e) {
    if (e.preventDefault) {
      e.preventDefault(); // Allows us to drop.
    }

    e.dataTransfer.dropEffect = 'move';

    return false;
  };

  handleDragEnter = function(e) {
    $(this).addClass('over');
  };

  handleDragLeave = function(e) {
    // this/e.target is previous target element.

    $(this).removeClass('over');
  };

  handleDrop = function(e) {
    // this/e.target is current target element.
    console.log(e.dataTransfer);

    if (e.stopPropagation) {
      e.stopPropagation(); // stops the browser from redirecting.
    }

    // Don't do anything if we're dropping on the same column we're dragging.
    if (dragSrcEl_ != this) {
      dragSrcEl_.innerHTML = this.innerHTML;
      this.innerHTML = e.dataTransfer.getData('text/html');
    }

    return false;
  };

  handleDragEnd = function(e) {
    // this/e.target is the source node.
    this.style.opacity = '1';

    [].forEach.call(cols_, function (col) {
      $(col).removeClass('over');
      $(col).removeClass('moving');
    });
  };

  [].forEach.call(cols_, function (col) {
    col.setAttribute('draggable', 'true');  // Enable columns to be draggable.
    col.addEventListener('dragstart', this.handleDragStart, false);
    col.addEventListener('dragenter', this.handleDragEnter, false);
    col.addEventListener('dragover', this.handleDragOver, false);
    col.addEventListener('dragleave', this.handleDragLeave, false);
    col.addEventListener('drop', this.handleDrop, false);
    col.addEventListener('dragend', this.handleDragEnd, false);
  });
});
.column {
  height: 150px;
  width: 150px;
  float: left;
  border: 2px solid #666666;
  background-color: #ccc;
  margin-right: 5px;
  border-radius: 10px;
  box-shadow: inset 0 0 3px #000;
  text-align: center;
  cursor: move;
  margin-bottom: 30px;
}
.column header {
  color: #fff;
  text-shadow: #000 0 1px;
  box-shadow: 5px;
  padding: 5px;
  background: linear-gradient(left center, rgb(0,0,0), rgb(79,79,79), rgb(21,21,21));
  border-bottom: 1px solid #ddd;
  border-top-left-radius: 10px;
  border-top-right-radius: 10px;
}
.column {
 transition: transform 0.2s ease-out;
}
.column.over {
  border: 2px dashed #000;
}
.column.moving {
  opacity: 0.25;
  transform: scale(0.8);
}
.column .count {
  padding-top: 15px;
  font-weight: bold;
  text-shadow: #fff 0 1px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
 
<div class="cols">
  <div class="column">
    <header>C</header>
  </div>
  <div class="column">
    <header>B</header>
  </div>
  <div class="column">
    <header>A</header>
  </div>
</div>


看起来供应商正在准备多个项目的拖放,但似乎没有默认设置。如果我错了,请纠正我。

我阅读了这些文章以更好地理解dnd

进一步阅读:


2
为了能够拖动多个元素,您必须首先选择它们。这是您自己要注意的事情。因此,在 dragstart 上自己将这些元素添加到列表中是很自然的。 - a better oliver
@zeroflagL 你说得对。但是有没有标准列表,可以添加项目呢?我现在感觉唯一的方法就是使用标记类(或其他什么)并动态选择这些项目。 - Nico O
2
标准支持文本和文件。请参见您的示例中的 setData。我个人将数据存储在外部(例如变量中)。这使我拥有所有自由,并且最终数据来自何处并不重要。您可以使用 setData 发送带有 ID 的序列化对象或数组(JSON.stringify / JSON.parse)。 - a better oliver
这个 Codepen 能解决问题吗?基于这篇文章: drag&drop - Rotan075
1
@Rotan075,虽然这很好玩且有趣,但它仍然是一个polyfill。它使用了当前选定和拖动元素的自己实现(请参见js代码中的第42行)。我想知道是否可能仅使用本地html5接口。非常感谢。 - Nico O
@Nico O 是的,没错!;) 这是一个很难的问题哈哈。不过我希望我能帮到你!! - Rotan075
1个回答

8
首先,在Chrome浏览器中,items长度以及files长度可以正常工作。你总是会得到0长度,因为拖放数据存储中的信息是受保护的,除非在拖放事件中。所以当你记录对象并在之后查看它时,你无法访问该信息。但如果你像这样记录它:
console.log(e.dataTransfer.items.length);

然后您将可以访问实际长度。有关保护模式的信息请参见此处:https://html.spec.whatwg.org/multipage/interaction.html#the-drag-data-store 这并不意味着它会给您拖动元素的数量,有几个原因:
首先,本地拖放API也用于在浏览器和其他应用程序之间拖放。因此,其中的许多功能都涉及这些情况。
另一个问题是,拖放API使您可以访问浏览器在拖放事物方面的默认行为。例如,在浏览器中拖动链接将打开该链接。当您使用API时,您正在访问这些行为。
此API的许多行为都是由此产生的,在这种意义上并不真正旨在拖动DOM元素。为此,其他库可能更合适,或者仅通过其提供的不同事件来管理内容本身。
由于您可以从其他应用程序拖放元素,因此很多信息与此有关。例如,在dataTransferItem中,您可以访问fileList对象。这仅适用于从操作系统拖动文件到浏览器时才有效。因此,您有文件计数和不同的文件被拖动。但是,这与拖动DOM元素无关。
您还可以从浏览器中拖动。例如,如果您想将HTML内容拖到Word中,则非常有用。但是,传输的信息比仅仅是DOM元素更复杂。在这种情况下,您正在拖动items,但这些items并不是DOM元素本身。它们是可以传输的不同类型的东西。
可以传输的类型因实现而异,但基本上可以有纯文本、HTML内容和链接。因此,项目的长度将是可用类型的长度。例如,在下面的jsfiddle中,在Chrome上,您可以拖动图像、链接和纯文本(对于此选项,您需要仅选择文本)。
 console.log(e.dataTransfer.items.length, 
e.dataTransfer.getData('text/plain'), 
e.dataTransfer.getData('text/uri-list'), 
e.dataTransfer.getData('text/html'));

当在灰色目标上拖动每个元素时,效果如下:
来自输入的纯文本:
length: 1 
 plain text: text 
 link:  
 html content: 

从图片中:

length: 2 
    plain text:  
     link: http://www.exiv2.org/include/img_1771.jpg 
     html content: <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"><img id="image1" src="http://www.exiv2.org/include/img_1771.jpg">

从链接中:

length: 3 
 plain text: http://google.com/ 
 link: http://google.com/ 
 html content: <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"><a href="http://google.com/">link 1</a>

请看这里:http://jsfiddle.net/ztj6t2ff/9/ 你会发现,html并不完全与你的页面相同。这是为了允许转移到其他应用程序并保持最佳格式。
所以一般来说,这个 API 允许您干涉默认行为,几乎可以将您在操作系统中拖动的任何东西拖入和从浏览器中拖出。在这个意义上,它相当复杂,但大多数功能与在同一页中拖动 DOM 元素无关。
实际上,几乎所有通过拖放进行的 DOM 操作都不使用此 API。jquery-ui draggable 例如根本不依赖于此 API。

非常感谢您详细的回答。这正是我在研究时所寻找的。我发布了这个问题来解决一个特定的用例,之后决定将其更加通用化。我使用了自定义JavaScript小部件来解决我的初始问题。我使用draggable="true"遇到了一些问题,因为:1)在IE中似乎无法禁用“helper”拖动图像。2)Firefox不会在dragover事件中包含鼠标位置等等。可拖动功能可能非常棒,但限制太多了。例如实现一个多拖出浏览器的功能。 - Nico O

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接