Adding Drag and Drop to JavaScript Apps

John Au-Yeung - Jan 19 '20 - - Dev Community

Subscribe to my email list now at http://jauyeung.net/subscribe/

Follow me on Twitter at https://twitter.com/AuMayeung

Many more articles at https://medium.com/@hohanga

In JavaScript, events are actions that happen in an app. They’re triggered by various things like inputs being entered, forms being submitted, changes in an element like resizing, or errors that happen when an app is running, etc.

We can assign event handlers to events so we can take perform an action when one is triggered. Events that happen to DOM elements can be handled by assigning an event handler to properties of the DOM object for the corresponding events. In this article, we will look at the ondragstart and ondrop event handlers.

ondragstart event

The ondragstart property of an HTML element lets us assign an event handler for the dragstart event which is triggered when the user starts dragging an element or text selection. For example, if we want to tracking when an element has started to be dragged and when it’s dropped, we can write the following HTML code:

<p id='drag-start-tracker'>
</p>
<div id='drag-box' draggable="true">
</div>
<div id='drop-zone'>
</div>

In the code above, we have a p element to show when something is dragged and the ID of the element that's being dragged. We have an element with the ID drag-box that's being dragged. Below that, we have a div with the ID drop-zone that will accept any element that is being dragged to be dropped in it. Then we can add the following CSS to style the HTML elements we added above:

#drag-box {  
  width: 100px;  
  height: 100px;  
  background-color: red;  
}

#drop-zone {  
  width: 200px;  
  height: 200px;  
  background-color: purple  
}

We can see that drag-box is red and drop-zone is purple and that drop-zone is bigger than drag-box. Next, we can assign our own event handler function to the ondragstart property for the DOM object representing the drag-box element to track it when it’s being dragged and update our drag-start-tracker p element to show that drag-box is being dragged with the following code:

const dragBox = document.getElementById('drag-box');
const dropZone = document.getElementById('drop-zone');
const dragStartTracker = document.getElementById('drag-start-tracker');
dragBox.ondragstart = (e) => {
  dragStartTracker.innerHTML = `Element with ID ${e.target.id} is being dragged.`;
};
dragBox.ondragend = (e) => {
  dragStartTracker.innerHTML = '';
  dropZone.appendChild(e.srcElement);
};

In the code above, we can see that ‘Element with ID drag-box is being dragged’ when the drag-box element is being dragged, and when we stop dragging the box and drop it into the drop-zone element, we should see that the text disappears and the drag-box element stays inside the drop-zone element. How it works is that when we first start dragging the drag-box element, the dragstart event will be fired and the dragBox.ondragstart will be called with the DragEvent object passed in, which has the e.target.id property that we reference to get the ID of the drag-box element.

When we release the mouse button when the drag-box is over the drop-zone , the event handler that we assigned to the ondragend event handler is called since the dragend event is triggered by releasing the mouse button, ending the dragging of the drag-box .

Inside the function, we have a DragEvent object passed in when the function is called and we can use the srcElement to get the DOM element object of the element being dragged, so we can use appendChild method of the dropZone object to append the drag-box element, which is what we get with the srcElement property to the drop-zone element.

ondrop event

We can set the ondrop property of a DOM element to handle the drop event for the element. The drop event is fired when an element or text selection is dropped into a valid drop target. For example, we can use it to make a div element that can be dragged to 2 different boxes and use the ondrop event handler to handle the dropping. First, we add the following HTML code to make our draggable div element and 2 div elements where we can drop the draggable div element into the following code:

<div id='drag-box' draggable="true">
</div>
<div id='drop-zones'>
  <div id='drop-zone'>
  </div>
  <div id='drop-zone-2'>
  </div>
</div>

Then we can add some CSS code to style the div elements with the following code:

#drag-box {
  width: 100px;
  height: 100px;
  background-color: red;
}

#drop-zone {
  width: 200px;
  height: 200px;
  background-color: purple
}

#drop-zone-2 {
  width: 200px;
  height: 200px;
  background-color: green
}

#drop-zones {
  display: flex;
}

In the code above, we make the drop-zone div elements side by side by add a div with the ID drop-zones to contain the 2 div elements inside. We use the display: flex CSS code to display drop-zone and drop-zone-2 div elements side by side. Then we change the background color of each div so we can distinguish them. Next, we add the JavaScript code to handle the dragging of the drag-box div elements and the dropping of it into either of the drop-zone elements with the with ondrop event handler that we define with the following code:

const dragBox = document.getElementById('drag-box');
dragBox.ondragstart = (e) => {
  e
    .dataTransfer
    .setData('text/plain', event.target.id);
};
document.ondragover = (e) => {
  e.preventDefault();
};
document.ondrop = (e) => {
  const id = e
    .dataTransfer
    .getData('text');
  e.srcElement.appendChild(document.getElementById(id));
}

The code above works by handling the ondragstart event handler to get the ID of the element that’s being dragged. The ondragstart handler is called when users start dragging the drag-box div . Inside the ondragstart event handler function that we defined, we called the e.dataTransfer.setData method to set the 'text' attribute of the DataTransfer object, which we need later when we’re dropping the drag-box inside one of the drop-zone or drop-zone-2 div elements. It’s very important that we have:

document.ondragover = (e) => {  
  e.preventDefault();  
};

This prevents the ondragover event handler from handling the event since once it’s handled with that event handler, then the drop event won’t be fired, and our ondrop event handler won’t be run. With that out of the way, we can define our ondrop event handler and then assign it to the document.ondrop property to handle the document’s drop event.

The event handler function has an e parameter which is a DragEvent object, which has some useful properties that we can use to handle the dropping of our drag-box element. Inside that event handler, we get the ID of the element that we’re dragging by calling the e.dataTransfer.getData method with the 'text' string to get the ID of the element that we’re dragging.

Then we can use the srcElement property of the e object to get the DOM element of which our drag-box div is being dropped and call appendChild on it with the document.getElementById(id) argument, where id should be 'drag-box' since that’s what should return from e.dataTransfer.getData(‘text’); since we set the ID of the drag-box element in the dragBox.ondragstart event handler.

Wrap Up

The ondragstart and ondrop properties are very useful for making drag and drop features in our web page. The ondragstart property lets us assign an event handler for the dragstart event, which is triggered when the user starts dragging an element or text selection. We can set the ondrop property of a DOM element to handle the drop event for the element. The drop event is fired when an element or text selection is dropped into a valid drop target.

Note that we have made set an event handler function for the ondragover property of document and call e.preventDefault() inside the function to prevent the ondragover event handler from handling the dragover event, which stops the drop event from triggering. With that out of the way, we can assign an event handler to the ondrop property to append the draggable element as a child to the drop target element.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .