HTML Drag and Drop

Wanted for Drag and Drop

We are all too familiar with the drag and drop of files, folder, and icons on our computer desktop. Drag and drop is a powerful and yet taken-for-granted user interface functionality of any desktop applications. With the help of pointing devices like mouse, drag and drop makes copying, insertion, and deletion of files and objects on your desktop a breeze. On the contrary, to achieve the same drag and drop on browsers, complex client-side scripting like JavaScript is required. This was the main reason that deterred the use of drag and drop in the many traditional web pages that we have seen so far. However, this is expected to change with the introduction of HTML Drag and Drop API which brings native support to the browser making it much easier to code. See for yourself how drag and drop in HTML works by getting your hands dirty.

Setting the Stage

Download and extract “html5dragndrop.zip” to your computer, you should find a file called “html5dragndrop.html”. View it on a browser as shown in Figure 1. You mission is to drag and drop the “Trash” into the “Dustbin”.

Figure 1: html5dragndrop.html on Browser

Figure 1: html5dragndrop.html on Browser

Open the “html5dragndrop.html” using a text editor and see the code as shown in Listing 1.

<!DOCTYPE HTML>
<html>
<head>
<title>HTML5 Drag and Drop API</title>
<style type="text/css">
#container {
  width: 100%;
  margin: auto;
  text-align:center;
}
#trash, #dustbin{
  display: inline-block;
  padding:10px;
  margin:10px;
}
#trash { 
  background-color: #DFD7D7; 
  width:50px; 
  height:50px; 
}
#dustbin { 
  background-color: #A347FF; 
  width:150px; 
  height:150px; 
}
</style>
<script>

</script>
</head>
<body>
<div id="container">
  <h2>HTML5 Drag and Drop</h2>
  <h4 id="status">Littering is an offence!</h4>
  <div id="dustbin">
    <p>Dustbin</p>
  </div>
  <div id="trash">
    <p>Trash</p>
  </div>
</div>
</body>
</html>

Your mission is to add the code to drag and drop the “Trash” into the “Dustbin” so as not to litter the place.  Follow me…

draggable=”true”

To make an element draggable, set its draggable attribute to true. Here, the draggable element is the <div id=”trash”>, you will enable it like this:

<div id="trash" draggable="true">

ondragstart=”dragStart(event)”

Next, you have to specify what should happen when the element is dragged by setting its ondragstart attribute to an event handler (function) that handles it. Here, the event handler is called dragStart(event). You may change it to any other name. Add this attribute like this:

<div id="trash" draggable="true" ondragstart="dragStart(event)">

dragStart(event)

The dragStart(event) event handler is actually a JavaScript function that is registered with the ondragstart attribute of a draggable element. It is called when the user starts dragging the element and is passed a dragEvent object (named as event in this example) as parameter. Add this function to the <script> section as shown:

<script>
function dragStart(event) 
{
  event.dataTransfer.setData("text/plain", event.target.id);
  document.getElementById("status").innerHTML = "Drag Start";
}
</script>

The dragStart(event) function can set the data for passing to the drop zone through the dataTransfer.setData(type, data) method (to be discussed in detail later) of the dragEvent object. In this exercise, the data parameter is assigned the id of the dragged div which is “trash” via the event.target.id, while the type parameter a MIME type called “text/plain” in this exercise. The type parameter can be any value that best describes the type of data and serves as an identifier for retrieving the data subsequently. I have added one line of code to display the status of the drag event, i.e. “Drag Start”, on the browser.

Save the code and refresh it on the browser, can you drag the trash now?

Figure 2: Drag Started!

Figure 2: Drag Started!

Yes, you can and the status shows “Drag Start” as shown in Figure 2. However, it cannot be dropped anywhere! You will have to add the code for dropping the trash. Move on…

ondragover=”dragOver(event)”

You can specify what should happen when a draggable element is being dragged over a drop zone by setting the ondragover attribute of this drop zone to an event handler (function) that handles it. In this exercise, the event handler is called dragOver(event). You may change to any other name. Here, the drop zone is the <div id=”dustbin”>, add this code to it:

<div id="dustbin" ondragover="dragOver(event)">

dragOver(event)

The dragOver(event) function is registered with the ondragover attribute of a drop zone. It is called when a dragged element is moved over the drop zone and is passed an Event object (named as event here) as parameter. Note that the browser default handling of a drag over event is to NOT allow a drop. So, to allow a drop, we have to override this default behavior by calling event.preventDefault(). Add the dragOver function in the <script> section as shown:

function dragOver(event) 
{
  event.preventDefault();
  document.getElementById("status").innerHTML = "Drag Over";
}

Save the code and refresh it on the browser,then drag the trash over the dustbin, what do you see?

Figure 3: Dragged Over!

Figure 3: Dragged Over!

Notice that the status now shows “Drag Over” as shown in Figure 3. However, it still cannot be dropped into the dustbin! Mission not accomplished yet. You have to add the code to instruct the dustbin to accept the trash. Move on…

ondrop=”drop(event)”

In order to accept the dropping of a draggable element onto a drop zone, you have to specify what should happen when that element is dropped onto it by setting the ondrop attribute of the drop zone, i.e. <div id=”dustbin”> in this exercise, to an event handler that handles it. Here, the event handler is simply called drop(event). You may change to any other name. Add this attribute like this:

<div id="dustbin" ondragover="dragOver(event)" ondrop="drop(event)">

drop(event)

The drop function is registered with the ondrop attribute of a drop zone. It is called when a draggable element is being released onto the drop zone at the end of a drag and is passed a dragEvent object (named as event here) as parameter. Add this function in the <script> section as shown:

function drop(event) 
{
  event.preventDefault();
  var data = event.dataTransfer.getData("text/plain");
  event.target.appendChild(document.getElementById(data));
  document.getElementById(data).innerHTML="Trash dumped!";
  document.getElementById("status").innerHTML = "Dropped";
}

The drop function can call the dataTransfer.getData(type) method of the dragEvent object to retrieve the dragged data that was set by dataTransfer.setData(type, data) method of the dragStart(event) function by passing it a type as parameter, i.e. “text/plain” in this exercise. The subsequent code simply drops (append) the dragged data, which contains the id of the dragged div, i.e. <div id=”trash”>, onto the drop zone, i.e. <div id=”dustbin”>. Note that the browser default handling of a drop event is to open a link to some URL. We can override this default behavior by calling event.preventDefault().

Mission Accomplished!

You should be able to drag the trash and drop it into the dustbin now as shown in Figure 4. Observe the change of statuses at the different stages of the drag and drop operation.

Figure 4: Trash Dumped!

Figure 4: Trash Dumped!

As a reward, the complete code for “html5dragndrop.html” is given in Listing 2.

<!DOCTYPE HTML>
<html>
<head>
<title>HTML5 Drag and Drop API</title>
<style type="text/css">
#container {
  width: 100%;
  margin: auto;
  text-align:center;
}
#trash, #dustbin{
  display: inline-block;
  padding:10px;
  margin:10px;
}
#trash { 
  background-color: #DFD7D7; 
  width:50px; 
  height:50px; 
}
#dustbin { 
  background-color: #A347FF; 
  width:150px; 
  height:150px; 
}
</style>
<script>
function dragStart(event) 
{
  event.dataTransfer.setData("text/plain", event.target.id);
  document.getElementById("status").innerHTML = "Drag Start";
}
function dragOver(event) 
{
  event.preventDefault();
  document.getElementById("status").innerHTML = "Drag Over";
}
function drop(event) 
{
  event.preventDefault();
  var data = event.dataTransfer.getData("text/plain");
  event.target.appendChild(document.getElementById(data));
  document.getElementById(data).innerHTML="Trash dumped!";
  document.getElementById("status").innerHTML = "Dropped";
}
</script>
</head>
<body>
<div id="container">
  <h2>HTML5 Drag and Drop</h2>
  <h4 id="status">Littering is an offence!</h4>
  <div id="dustbin" ondragover="dragOver(event)" ondrop="drop(event)">
    <p>Dustbin</p>
  </div>
  <div id="trash" draggable="true" ondragstart="dragStart(event)">
    <p>Trash</p>
  </div>
</div>
</body>
</html>

Diving Deeper…

A total of seven events are being fired at different stages of a drag and drop operation, you have already met some of them in your first drag and drop exercise above. These events can be monitored and captured through event listeners attached to the draggable elements or the drop zone as attributes. For example, in the preceding exercise, the event listener for drag start event is ondragstart. Each event listener will be assigned a function to perform when the event arises, such as the dragStart(event) function is being assigned to ondragstart event listener in the preceding exercise.

I have put together these various events and their corresponding listeners in Table 1. Code snippets are included to illustrate their usage. Note that the upper three rows are events and listeners for draggable elements, whereas the rest are for drop zones or droppable elements. You should test out the code snippets by adding them to the “html5dragndrop.html” and view their effects on a browser.

Table 1: Drag and Drop Events and Event Listeners
Event Listener Description / Code Snippet
dragstart ondragstart The dragstart event is triggered when the user starts dragging the dragged element and is captured by ondragstart listener.

The dragStart function essentially sets the type, e.g.  a MIME type such as “text/plain”, and the value of the dragged data, e.g. event.target.id which is the id of the dragged div, through the dataTransfer.setData() method of the Event object.

<div id="trash" draggable="true" ondragstart="dragStart(event)">
function dragStart(event) 
{
  event.dataTransfer.setData("text/plain", event.target.id);
  document.getElementById("status").innerHTML = "Drag Start";
}
drag ondrag The drag event is triggered every time the element is being dragged and is captured by ondrag listener.

<div id="trash" draggable="true" ondragstart="dragStart(event)" ondrag="drag(event)">
function drag(event) 
{
  document.getElementById("status").innerHTML = "Dragging...";
}
dragend ondragend The dragend event is triggered when the user releases the mouse button while dragging an element  and is captured by ondragend listener.

<div id="trash" draggable="true" ondragstart="dragStart(event)" ondragend="dragEnd(event)">
function dragEnd(event) 
{
  document.getElementById("status").innerHTML = "Drag End";
}
dragenter ondragenter The dragenter event is triggered when the mouse is first moved over the droppable element while a drag is occurring and is captured by ondragenter listener. Note that the browser default handling of a dragenter event is to NOT allowed a drop. So, to allow a drop, we have to override this default behavior by calling event.preventDefault().

<div id="dustbin" ondragover="dragOver(event)" ondrop="drop(event)" ondragenter="dragEnter(event)">
function dragEnter(event) 
{
  event.preventDefault();
  document.getElementById("status").innerHTML = "Drag Enter";
}
dragover ondragover The dragover event is triggered when the mouse is moved over the droppable element while a drag is occurring and is captured by ondragover listener. Often, the operation that triggers this listener is the same as that for dragenter event.

Note that the browser default handling of a dragover event is to NOT allowed a drop. So, to allow a drop, we have to override this default behavior by calling event.preventDefault().

<div id="dustbin" ondragover="dragOver(event)" ondrop="drop(event)" ondragenter="dragEnter(event)">
function dragOver(event) 
{
  event.preventDefault();
  document.getElementById("status").innerHTML = "Drag Over";
}
dragleave ondragleave The dragleave event is triggered when the draggable element left the droppable zone and is captured by ondragleave listener.

<div id="dustbin" ondragover="dragOver(event)" ondrop="drop(event)" ondragleave="dragLeave(event)">
function dragLeave(event) 
{
  document.getElementById("status").innerHTML = "Drag Leave";
}
drop ondrop The drop event is triggered when the draggable element is being dropped onto the droppable element at the end of a drag and is captured by ondrop listener.

The drop function essentially calls the dataTransfer.getData() method of the dragEvent object to retrieve the dragged data that was set by dataTransfer.setData() method of the dragStart function by passing it a type as parameter, e.g. “text/plain”. The subsequent code will do the necessary operation to the dragged data, such as appending the it to some HTML element.

Note that the browser default handling of a drop event is to open a link to some URL. We can override this default behavior by calling event.preventDefault().

<div id="dustbin" ondragover="dragOver(event)" ondrop="drop(event)">
function drop(event) 
{
  event.preventDefault();
  var data = event.dataTransfer.getData("text/plain");
  event.target.appendChild(document.getElementById(data));
  document.getElementById(data).innerHTML="I'm dumped";
  document.getElementById("status").innerHTML = "Dropped";
}

DataTransfer Object

The functions for all the drag and drop events accept an Event parameter that has a dataTransfer property. The event.dataTransfer returns a DataTransfer object that holds the data that is being dragged during a drag and drop operation. This data can be set and retrieved via the various properties and methods associated with the DataTransfer object as explained in Tables 2 and 3.

Table 2: dataTransfer Properties
Property Description / Code Snippet
dropEffect The dropEffect property specifies one of the four possible values for drop effect of the dragenter and dragover events and should always be one of the possible value set by the effectAllowed property.

The four possible values are:

  • copy: A copy of the dragged item is made at the new location.
  • move: The dragged item is moved to a new location.
  • link: A link is established to the dragged item at the new location.
  • none: The item can not be dropped.
function dragEnter(event) 
{
  event.dataTransfer.dropEffect = 'move';
}
effectAllowed The effectAllowed property specifies the effects that are allowed for a drag by setting it in the dragstart event.

The possible values are:

  • copy: A copy of the dragged item may be made at the new location.
  • move: The dragged item may be moved to a new location.
  • link: A link may be established the dragged item at the new location.
  • copyLink: A copy or link operation is permitted.
  • copyMove: A copy or move operation is permitted.
  • linkMove: A link or move operation is permitted.
  • all: All operations are permitted.
  • none: The item can not be dropped.
  • uninitialized: This is the default value when no effect is set, which is equivalent to all.
function dragStart(event) 
{
  event.dataTransfer.effectAllowed = 'move';
}
types The types property returns the list of formats that are set in the dragstart event.
files The files property returns the list of files being dragged, if any.
Table 3: dataTransfer Methods
Method Description / Code Snippet
clearData() The clearData() method takes a type as parameter and removes the data associated with the type. The type parameter is optional. If the type is omitted, all data is removed.

event.dataTransfer.clearData('Text');
setData() The setData() method sets the data for a given type. The first parameter is the type and the second parameter the data.

event.dataTransfer.setData("text/plain", event.target.id);
getData() The getData() method takes a type as parameter and retrieves the data for the type, or an empty string if data for that type does not exist or the dataTransfer object contains no data.

var data = event.dataTransfer.getData("text/plain");
setDragImage() The setDragImage() method is used to set a custom image to be used during the dragging process. If this is not set, the default image is created from the source that is being dragged.

The setDragImage() method takes three parameters:

  • Image source
  • x coordinate offset into the image where the mouse cursor should be
  • y coordinate offset into the image where the mouse cursor should be

For example, to center the image, set the x and y coordinate offset values to half of the width and height of the image respectively.

function dragStart(event)
{   
  event.dataTransfer.setDragImage(document.getElementById('imageid'),50,50);
}
addElement() The addElement() method is used to set a data source for the drag and drop operation. The default data source is the draggable element itself.

Drag and Drop Animals

Let’s wrap up the HTML5 Drag and Drop API by creating a simple web page for dragging and dropping animal images into boxes bearing their names as shown in Figure 5. If an animal image goes to a box bearing the wrong name, it will not be allowed to drop there.

Figure 5: Drag and Drop Animals

Figure 5: Drag and Drop Animals

This web page is named as “drag_n_drop_animals.html”. The code  is shown in Listing 3 and is available for download.

<!DOCTYPE HTML>
<html>
<head>
<title>Drag and Drop Animals</title>
<style>
body {
  text-align:center; 
}
.animal_name, .animal_image {
  width: 800px;
  margin: auto;
}
.animal_name > div, .animal_image > div {
  float: left;
  padding: 10px;
  margin:10px;
  width: 350px;
  height: 210px;
}
.animal_name > div {
  border: 5px solid red
}
.animal_image > div {
  border: 1px solid blue
}
</style>
<script>
function dragOver(event)
{
  event.preventDefault();
}
function dragStart(event)
{ 
  event.dataTransfer.setData("image_name", event.target.id);
}
function drop(event)
{
  event.preventDefault();

  var data=event.dataTransfer.getData("image_name");
 
  if (data=="panda_image" && event.target.id=="panda_name")
  {
    event.target.innerHTML="";
    event.target.appendChild(document.getElementById(data));
  }
  else if (data=="tortoise_image" && event.target.id=="tortoise_name")
  {
    event.target.innerHTML="";
    event.target.appendChild(document.getElementById(data)); 
  }
}
</script>
</head>
<body>
<h1>Drag and Drop Animals</h1>
<div class="animal_name">
  <div id="panda_name" ondrop="drop(event)" ondragover="dragOver(event)"><h2>Panda</h2></div>
  <div id="tortoise_name" ondrop="drop(event)" ondragover="dragOver(event)"><h2>Tortoise</h2></div>
</div>
<br>
<div class="animal_image">
  <div><img id="tortoise_image" src="tortoise.jpg" draggable="true" ondragstart="dragStart(event)" width="325" height="206"></div>
  <div><img id="panda_image" src="panda.jpg" draggable="true" ondragstart="dragStart(event)" width="325" height="206"></div>
</div>
</body>
</html>

I will elaborate on the logic for determining whether to accept or reject an animal image.

  • First, in the dragStart() function, I assign “image_name” as  the type, and the id of the dragged image obtainable from event.target.id as the data to the event.dataTransfer.setData() function.
    function dragStart(event)
    { 
      event.dataTransfer.setData("image_name", event.target.id);
    }
  • Then, in the drop() function, the code will look for the data of the droppable element identified by the type “image_name”, i.e.
    var data = event.dataTransfer.getData("image_name")
  • The data of the droppable image id, i.e. “panda_image” or “tortoise_image”,  obtained above and the id of the drop zone, i.e. “panda_name” or “tortoise_name”, obtainable from event.target.id of the drop() function will collectively determine if an animal image is allowed to drop into the drop zone.
    function drop(event)
    {
      event.preventDefault();
     
      var data=event.dataTransfer.getData("image_name");
     
      if (data=="panda_image" && event.target.id=="panda_name")
      {
        event.target.innerHTML="";
        event.target.appendChild(document.getElementById(data));
      }
      else if (data=="tortoise_image" && event.target.id=="tortoise_name")
      {
        event.target.innerHTML="";
        event.target.appendChild(document.getElementById(data)); 
      }
    }

That’s all for the HTML Drag and Drop API.

Download

Posted in HTML / CSS Tagged with: , , ,