Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drag and drop Multiple html elements at once

Is there a HTML5 default way for dragging and dropping -multiple- elements into another html element on the same page?

It seems the HTML5 attribute draggable, which applies to one element says: "You can drag me, but you'd need multiple input devices to select another draggable element at the same time."

As a workaround one can add identifiers to selected elements using js and get all selected elements on a certain event and do what we want. But is this really the "correct" way?

When you inspect the drop event there is an interface for multiple files, when handling file drag and drop. And there is also the items property- at least in Chrome.

There are differences across browsers.

In Chrome

The drop event contains the items property

dataTransfer: DataTransfer
    items: DataTransferItemList
        length: 0

Items seems to be 0, no matter if you dragged an item or not.

In Firefox

The drop event contains the mozItemCount property

dataTransfer: DataTransfer
    mozItemCount: 1

mozItemCount seems to be at least 1.


Here is a little demo

Where you can observe your developer tools console:

This source was taken from here: 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>

So it looks like the vendors are preparing for multiple item drag and drop, but there seems no be no default. Please correct me if i'am wrong.

I read these articles to better understand dnd

  • https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Drag_and_drop
  • http://html5doctor.com/native-drag-and-drop/

Further readings:

  • Definition of DataTransferItemList
  • MDN's take on the DataTransfer interface
  • MDN's non standard approach of multiple drag and drop
like image 319
Nico O Avatar asked Sep 25 '15 11:09

Nico O


People also ask

How do you do multiple drag-and-drop?

Press and hold the Control Key (on the keyboard). While holding the Ctrl Key, select another file. Repeat step 2 until all the required files are selected. Note: It is important to keep the Ctrl Key pressed to ensure all files are highlighted.

What is Ondrop in HTML?

The ondrop attribute fires when a draggable element or text selection is dropped on a valid drop target. Drag and drop is a very common feature in HTML5. It is when you "grab" an object and drag it to a different location. For more information, see our HTML Tutorial on HTML5 Drag and Drop.


1 Answers

First of all, the items length as well as files length is working in Chrome. You always get 0 length because the info in the drag data store is protected, except on drop event. So when you log the object and look at it afterwards, you don't have access to the info. But if you log it like this:

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

Then you'll have access to the actual length. See here regarding protected mode:

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

That is not to say that it'll give you the count of dragged elements, and there are a few reasons as to why:

First thing is that the native drag and drop API is also used for dragging from the browser to other applications and vice versa. So lots of the features in there deal with these cases.

Other thing is that drag and drop API gives you access to things that are defaults behaviors of the browser in regard to dragging things. For example, dragging a link in your browser will open the link. When you use the API, you're accessing these behaviors.

Many behaviors of this API are a consequence of this and in that sense are not really intended to drag DOM elements around. To this purpose, other libraries are probably more appropriate or simply managing the content itself through the different events it offers.

Since you can drag and drop elements from other applications, a lot of the info has to do with this. For example in dataTransferItem, you have access to a fileList object. This is only working when dragging files from your OS into the browser. So you have a count of files, and the different files dragged. But this has nothing to do with dragging of DOM elements.

You can also drag from the browser. Very useful for example if you want to drag HTML content to Word. But then the info to transfer is more complex than simply the DOM elements. In that case you're dragging items, but these items are not the DOM elements per say. They are the different types of things that can be transfered.

The types of things that can be transfered varies a lot in implementation, but basically you can have plain text, html content and links. So the length of the items will be the length of types available. For example, in the jsfiddle below, on Chrome, you can drag images, links and plain text(for this one you need to select only the text). Result of :

 console.log(e.dataTransfer.items.length, 
e.dataTransfer.getData('text/plain'), 
e.dataTransfer.getData('text/uri-list'), 
e.dataTransfer.getData('text/html'));

when draggin each element on the grey target is this:

Plain text from the input :

length: 1 
 plain text: text 
 link:  
 html content: 

From image:

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

From link:

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>

See here: http://jsfiddle.net/ztj6t2ff/9/

You'll see that the html is not exactly what you have on your page. This is to allow transfer to other applications and keep the best formatting possible.

So in general, this API allows you to interfere with default behaviors of dragging almost anything you can drag in your OS in and from the browser. In that sense it's pretty complex, but most of the features have nothing to do with dragging DOM elements inside a same page.

And in fact almost all DOM manipulations through drag and drop are made without using this API. jquery-ui draggable for example doesn't rely at all on this API.

like image 183
Julien Grégoire Avatar answered Oct 26 '22 18:10

Julien Grégoire