Перетаскивание нескольких элементов html сразу

Есть ли способ HTML5 по умолчанию для перетаскивания-многократных элементов в другой элемент html на одной странице?

Кажется, что атрибут HTML5 draggable, который относится к одному элементу, говорит: "Вы можете перетащить меня, но вам потребуется несколько устройств ввода для одновременного выбора другого перетаскиваемого элемента".

В качестве обхода можно добавить идентификаторы к выбранным элементам с помощью js и получить все выбранные элементы на определенном событии и делать то, что мы хотим. Но действительно ли это "правильный" способ?

При проверке события drop существует интерфейс для нескольких файлов при обработке перетаскивания файлов. И есть свойство items - по крайней мере, в Chrome.

В браузерах есть различия.

В 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>

Ответы

Ответ 1

Прежде всего, длина текста items, а также длина files работает в Chrome. Вы всегда получаете длину 0, потому что информация в хранилище данных перетаскивания защищена, за исключением события drop. Поэтому, когда вы регистрируете объект и смотрите на него потом, у вас нет доступа к информации. Но если вы зарегистрируете его следующим образом:

console.log(e.dataTransfer.items.length);

Тогда у вас будет доступ к фактической длине. См. Здесь protected mode:

https://html.spec.whatwg.org/multipage/interaction.html#the-drag-data-store

Это не означает, что он даст вам количество перетаскиваемых элементов, и есть несколько причин, почему:

Прежде всего, родной drag and drop API также используется для  перетаскивание из браузера в другие приложения и наоборот. Так  многие из этих функций имеют дело с этими случаями.

Другое дело, что API перетаскивания дает вам доступ к вещам, которые  по умолчанию поведение браузера в отношении перетаскивания вещей. Например, перетаскивание ссылки в вашем браузере откроет ссылку. когда  вы используете API, вы получаете доступ к этим типам поведения.

Многие действия этого API являются следствием этого и в этом смысле на самом деле не предназначены для перетаскивания элементов DOM. С этой целью другие библиотеки, вероятно, более подходят или просто управляют самим контентом через различные события, которые он предлагает.

Поскольку вы можете перетаскивать элементы из других приложений, эта информация связана с большим количеством информации. Например, в dataTransferItem у вас есть доступ к объекту fileList. Это работает только при перетаскивании файлов из вашей ОС в браузер. Таким образом, у вас есть количество файлов, и разные файлы перетаскиваются. Но это не имеет никакого отношения к перетаскиванию DOM elements.

Вы также можете перетащить из браузера. Очень полезно, например, если вы хотите перетащить содержимое 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="#" onclick="location.href='http://google.com/'; return false;">link 1</a>

Смотрите здесь: http://jsfiddle.net/ztj6t2ff/9/

Вы увидите, что html не совсем то, что у вас есть на вашей странице. Это позволяет переносить другие приложения и поддерживать наилучшее форматирование.

В общем, этот API позволяет вам вмешиваться в поведение по умолчанию при перетаскивании всего, что вы можете перетащить в свою ОС в браузере и из него. В этом смысле это довольно сложно, но большинство функций не имеют ничего общего с перетаскиванием элементов DOM внутри одной и той же страницы.

И фактически почти все манипуляции с DOM посредством перетаскивания выполняются без использования этого API. jquery-ui draggable, например, не полагается вообще на этот API.