Codementor Events

Drag and Drop with HTML5

Published Sep 14, 2020
Drag and Drop with HTML5

This article first appeared on BackTicks & Tildes.

Drag and Drop is a very common feature in today’s web applications. It has been with us for over a decade. This feature enables the movement of objects from one location to another on a web page.

A very popular app that implements this feature is Trello. The extra level of interaction that the drag and drop feature offers can lead to a wide array of awesome implementations in web applications.

In this article, we’ll be implementing simple drag and drop using native HTML 5.

How it works

Implementing the drag and drop feature might seem complicated but it generally revolves around 4 major points:

  1. Make an element draggable.

Making an element draggable is as simple as setting the draggable attribute of an element to true.

<img src=”img.png” draggable=”true” />
  1. What happens when an element is dragged?

Once you have a draggable element, you can call an ondragstart event handler and pass it a function that does whatever you want.

  1. Where to drop an element?

You can drop an element on any spot as long as you make that element a valid ‘drop zone’. You can do that via the element’s ondragover event handler.

  1. What happens when an element is dropped?

Lastly, you can determine what happens when an element is dropped on a ‘drop zone’ via the element’s ondrop event handler.

Now that we’ve laid out the basics, let’s create our demo drag and drop app.

Setting up the demo application

For our simple app, we’ll need 3 files:

  • an index.html file
  • a main.css file
  • a main.js file

Copy the following code into the index.html file:

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>Drag n Drop</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" type="text/css" media="screen" href="main.css" />
  <link href="https://fonts.googleapis.com/css?family=Titillium+Web" rel="stylesheet">
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.10/css/all.css" integrity="sha384-+d0P83n9kaQMCwj8F4RJB66tzIwOKmrdb46+porD/OvrJ+37WqIM7UoBtwHO6Nlg"
    crossorigin="anonymous">
  <link rel="stylesheet" href="http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
  <script src="main.js"></script>
</head>

<body>
  <div class="container">
    <div class="droppable">
      <div id='list' class="list" draggable="true" ondragstart="dragStart(event)">
        <div class="heading">
          <h4 class="list-title">List Heading</h4>
        </div>
        <div class="cards">
          <div class="list-card">
            <p>Card title</p>
            <span class="js-badges">
              <div class="badge is-complete">
                <span class="badge-icon">
                  <i class="ion-android-checkbox-outline fa-lg pr-1"></i>
                </span>
                <span class="badge-text">7/7</span>
              </div>
            </span>
          </div>
        </div>
      </div>
    </div>
    <div class="droppable" ondragover="allowDrop(event)" ondrop="drop(event)"></div>
    <div class="droppable" ondragover="allowDrop(event)" ondrop="drop(event)"></div>
    <div class="droppable" ondragover="allowDrop(event)" ondrop="drop(event)"></div>
    <div class="droppable" ondragover="allowDrop(event)" ondrop="drop(event)"></div>
    <div class="droppable" ondragover="allowDrop(event)" ondrop="drop(event)"></div>
    <div class="droppable" ondragover="allowDrop(event)" ondrop="drop(event)"></div>
    <div class="droppable" ondragover="allowDrop(event)" ondrop="drop(event)"></div>
  </div>
</body>

</html>

Copy the accompanying styles into the main.css file:

.container {
  display: grid;
  grid-template-columns: auto auto auto auto;
  grid-column-gap: 1rem;
  grid-row-gap: 3rem;
  font-family: 'Titillium Web', sans-serif;
}

.droppable {
  width: 15rem;
  height: 15rem;
  max-height: 15rem;
  border: 1px dotted #7f8082;
  border-radius: 3px;
  padding: 0.5rem;
}

p {
  margin: 0;
  padding: 0;
}

.list {
  background: #e2e4e6;
  width: 100%;
  height: 100%;
  border-radius: 3px;
  cursor: pointer;
}

.list-title {
  margin: 0;
  padding: 0.4rem 0.7rem;
}

.cards {
  padding: 0.6rem;
}

.list-card {
  background: #FFFFFF;
  border-radius: 3px;
  padding: 6px 6px 2px 8px;
  margin-bottom: 0.1rem;
}

.list-card p {
  font-size: 0.85rem;
  margin-bottom: 0.2rem;
}

.badge.is-complete {
  background-color: #61bd4f;
  color: #fff;
  border-radius: 3px;
  padding: 0.3rem 0.5rem;;
  font-size: 0.7rem;
  display: inline-block;
}

Visiting the HTML page in the browser, you should something like this:
1_oEidRzo91mvOZnzOafgZuw.png

In our HTML markup, we have a div with the id ‘list’. This div is our ‘draggable’ card as we have set it’s draggable attribute to true. It also has functions passed to its ondragstart event handler.
Also, on in our HTML markup, we have 7 divs which all have functions passed to their ondragover and ondrop event handlers. These are our ‘drop zones’.

Implementing drag and drop

The drag and drop API has a dataTransfer object that holds the data that is being dragged during a drag and drop operation. When we drag our card, we call the dataTransfer.setData() method to set the data we want to drag.
In the function being called by the ‘draggable’ card’s ondragstart event hander, we add the following code to our main.js file:

const dragStart = (event) => {
  event.dataTransfer.setData("text/plain", event.target.id);
}

In the above code, we’re setting the data type as “text/plain” and the value of the element to drag as the id of the dragged element. With this, we should be able to drag our card.
To declare a place where we can drop our card, we add the following code to the function being called by the ondragover event handler of our ‘drop zones’:

const allowDrop = (event) => {
  event.preventDefault();
  event.currentTarget.style.background = '#7f8082';
}

This ensures that the browser does not carry out it’s default action when a draggable element is being dragged over a valid ‘drop zone’. We can carry out other actions here, like changing the background-color property of the ‘drop zone’ so the user is aware they can drop an element there.

Lastly, now that we have a valid drop zone, we go ahead and drop our element. To do this, we get the data we want to drop from the dataTransfer.getData() method. The getData method retrieves the data for a given type, or an empty string if data for that type does not exist or the data transfer contains no data.

Add the following code to your main.js file:

const drop = (event) => {
  event.preventDefault();
  const data = event.dataTransfer.getData("text/plain");
  const element = document.querySelector(`#${data}`);
  event.currentTarget.style.background = 'white'
  try {
    event.target.appendChild(element);
  } catch (error) {
    console.warn("you can't move the item to the same place")
  }
}

In the code above, we get the element we want to drop via the dataTransfer.getData() method, we find the element via its id and append it to the ‘drop zone’. We perform the appending in a try/catch block so we can catch any error that occurs while appending the element to the ‘drop zone’. Such errors can come from trying to drop an element on the same spot you dragged it from.

Your main.js should now look like this:

const dragStart = (event) => {
  event.dataTransfer.setData("text/plain", event.target.id);
}

const allowDrop = (event) => {
  event.preventDefault();
  event.currentTarget.style.background = '#7f8082';
}

const drop = (event) => {
  event.preventDefault();
  const data = event.dataTransfer.getData("text/plain");
  const element = document.querySelector(`#${data}`);
  event.currentTarget.style.background = 'white'
  try {
    event.target.appendChild(element);
  } catch (error) {
    console.warn("you can't move the item to the same place")
  }
}

There you have it, our drag and drop is fully functional. If you reload the page now, you should be able to drag the card from one place to another on the page.

There’s more to the drag and drop API than we have covered here. For example, when you drag the card in our demo app over a ‘drop zone’ but don’t drop it there, the drop zone’s background colour doesn’t change back to it’s original colour. We can add an ondragleave event handler to change the colour when a draggable item leaves our ‘drop zones’.

I hope you found this tutorial useful. Do share this article so others can see it.

Discover and read more posts from Chuks Opia
get started
post comments1Reply
Blade Nelson
4 years ago

Awesome article. Thanks for sharing Chuks!