Drag and Drop with JavaScript

One of the greatest interface solutions of JavaScript is known as “Drag and Drop”. In this chapter, we are going to find out what it is mainly used for, and why it should be an essential part of your work.

Dragging and dropping something is a clear and straightforward way of doing multiple things: from copying and moving your documents to ordering ( for example, dropping an item into a cart).

Modern HTML standard includes a specific section with events like dragstart, dragend,and more. The handiest thing about these events is that they can help you solve simple tasks much easier. For example, with the help of them, you can handle the drag and drop of “external” files into your browser, in a way that you can take any file in the OS file manager and drop it into the browser window, thus giving JavaScript access to its contents.

However, there are limitations to native Drag events. For example, they don’t allow you to limit dragging by a certain area. Another limitation is that you can’t make it “vertical” or “horizontal” only. Other drag and drop tasks can’t be implemented by that API either.

Now, let’s see how to perform drag and drop with mouse events.

The Algorithm of Drag and Drop

The principal drag and drop algorithm looks as follows:

  • On the mousedown, you need to arrange the element for moving, if it is necessary ( for example, you can create its copy).
  • On the mousemove, you should move it by changing the left/top, as well as position:absolute.
  • On the mouseup, implement the overall actions connected to a finished drag and drop.

Now, you know the basics. Let’s get to the examples.

For instance, a drag and drop algorithm of a text will look like this:

<!DOCTYPE html>
<html>
  <head>
    <title>Title of the Document</title>
  </head>
  <body>
    <p id='text'>Drag the text</p>
    <script>
      text.onmousedown = function(event) { //  start the process
        // get ready to move: make an absolute and top z-index
        text.style.position = 'absolute';
        text.style.zIndex = 1000;
        // move it from any existing parents directly to the body
        // to position it relative to the body
        document.body.append(text);
        // and put this absolutely positioned text under the pointer
        moveAt(event.pageX, event.pageY);
        // centers the text on the coordinates (pageX, pageY)
        function moveAt(pageX, pageY) {
          text.style.left = pageX - text.offsetWidth / 2 + 'px';
          text.style.top = pageY - text.offsetHeight / 2 + 'px';
        }
        function onMouseMove(event) {
          moveAt(event.pageX, event.pageY);
        }
        //  move the text on mousemove
        document.addEventListener('mousemove', onMouseMove);
        // drop the text, remove unneeded handlers
        document.onmouseup = function() {
          document.removeEventListener('mousemove', onMouseMove);
          text.onmouseup = null;
        };
      };
    </script>
  </body>
</html>

After running the code above, you can notice a strange thing: at the start of the process, the text “forks”, and you begin dragging its clone. Such behavior shows up because the browser contains its own drag and drop for different elements such as images that run automatically and conflicting with yours.

So, for disabling it, you need to use the following code:

text.ondragstart = function () {
  return false;
};

There is another important aspect to note: you track mousemove on the document and not the text. But, mousemove triggers frequently, but not for each pixel. Hence, after the move, your pointer might jump from the text anywhere in the heart of the document., and outside of the window, as well.

Correct Positioning

As you noticed in the example above, the text always moves in a way that its center is under the pointer, like this:

text.style.left = pageX - text.offsetWidth / 2 + 'px';
tex.style.top = pageY - text.offsetHeight / 2 + 'px';

However, there is a side-effect here. For initiating the drag and drop, you should mousedown anywhere you want, on the text.

For example, if you begin to drag by the edge of the text, the pointer should be kept over the edge throughout the dragging.

You can also update the algorithm following the steps below:

  1. At the moment a visitor presses the mousedown button, save the distance from the pointer to the text’s left-upper corner in the variables shiftX/shiftY. That distance should be kept while dragging.

    Let’s subtract the coordinates to get these shifts, like here:

    // onmousedown
    let shiftX = event.clientX - text.getBoundingClientRect().left;
    let shiftY = event.clientY - text.getBoundingClientRect().top;
  2. As the next step, while dragging, position the text on the same shift that is relative to the pointer, as follows:
    // onmousemove
    // text has position:absolute
    text.style.left = event.pageX - shiftX + 'px';
    text.style.top = event.pageY - shiftY + 'px';

So, the final and better positioning is demonstrated below:

<!DOCTYPE html>
<html>
  <head>
    <title>Title of the Document</title>
    <style>
      #text {
        cursor: pointer;
        cursor: pointer;
        width: 40px;
        height: 40px;
      }
    </style>
  </head>
  <body>
    <p id='text'>Drag the text</p>
    <script>
      text.onmousedown = function(event) {
        let shiftX = event.clientX - text.getBoundingClientRect()
          .left;
        let shiftY = event.clientY - text.getBoundingClientRect()
          .top;
        text.style.position = 'absolute';
        text.style.zIndex = 1000;
        document.body.append(text);
        moveAt(event.pageX, event.pageY);
        // move the text along the coordinates (pageX, pageY)
        // taking into account the initial shifts
        function moveAt(pageX, pageY) {
          text.style.left = pageX - shiftX + 'px';
          text.style.top = pageY - shiftY + 'px';
        }
        function onMouseMove(event) {
          moveAt(event.pageX, event.pageY);
        }
        // move the text to the  mousemove
        document.addEventListener('mousemove', onMouseMove);
        // drop the text, remove unneeded handlers
        text.onmouseup = function() {
          document.removeEventListener('mousemove', onMouseMove);
          text.onmouseup = null;
        };
      };
      text.ondragstart = function() {
        return false;
      };
    </script>
  </body>
</html>

The d distinction between the past cases and this one is obvious: in the previous example, the text jumps under the pointer.

Potential Drop Targets (Droppables)

In the examples above, the text was dropped anywhere to stay. But, in practice, it is usually necessary to take an element and drop it into another (for example, a file into a folder).

In order to implement drag and drop functionality in JavaScript, we need to define the potential drop targets, also known as droppables. These are the elements on the page where the user can drop the dragged element.

Here's the HTML part:

<div id="draggable1" class="draggable" draggable="true">
  Drag me!
</div>

<div id="droppable1" class="droppable">
  Drop here!
</div>

This is the HTML code for our draggable and droppable elements. We have created a div element with id attribute set to "draggable1" and a class name of "draggable". We have also set the draggable attribute to "true", which makes the element draggable.

We have also created a div element with id attribute set to "droppable1" and a class name of "droppable". This will serve as the drop target for our draggable element.

And here's the CSS part:

.draggable {
  width: 100px;
  height: 50px;
  background-color: lightblue;
  border: 1px solid black;
  padding: 10px;
}

.droppable {
  width: 200px;
  height: 100px;
  background-color: white;
  border: 1px solid black;
  padding: 10px;
}

This is the CSS code that styles our draggable and droppable elements. We have set the width, height, background color, border, and padding for our elements. You can customize these styles as per your needs.

And finally let's take a look at the JavaScript code that make this all happen:

var draggable = document.getElementById("draggable1");
var droppable = document.getElementById("droppable1");

draggable.addEventListener("dragstart", function(event) {
  event.dataTransfer.setData("text", event.target.id);
});

droppable.addEventListener("dragover", function(event) {
  event.preventDefault();
});

droppable.addEventListener("drop", function(event) {
  event.preventDefault();
  var data = event.dataTransfer.getData("text");
  var draggableElement = document.getElementById(data);
  droppable.appendChild(draggableElement);
});

We first get references to our draggable and droppable elements using their id attributes.

We then add an event listener to our draggable element for the dragstart event. Inside this event listener, we set the dataTransfer property of the event object to the id of the draggable element using the setData() method. This will allow us to access the id of the draggable element when it is dropped onto the droppable element.

We then add an event listener to our droppable element for the dragover event. Inside this event listener, we prevent the default action of the event using the preventDefault() method. This is required for the drop event to work correctly.

Finally, we add an event listener to our droppable element for the drop event. Inside this event listener, we prevent the default action of the event using the preventDefault() method. We then get the id of the draggable element from the dataTransfer property of the event object using the getData() method.

We then get a reference to the draggable element using its id and append it to the droppable element using the appendChild() method. This will move the draggable element inside the droppable element when it is dropped onto it.

Now that we have each part, let's take the final step and put it all together and see the result:

<!DOCTYPE html>
<html>
  <head>
    <title>Title of the Document</title>
    <style>
      #text {
        cursor: pointer;
        cursor: pointer;
        width: 40px;
        height: 40px;
      }
      .droppable {
        background-color: white;
        border: 1px solid black;
        padding: 10px;
      }

      .droppable.drag-over {
        background-color: lightblue;
      }

      .droppable.dropped {
        background-color: lightgreen;
      }
    </style>
  </head>
  <body>
    <div id="draggable1" class="draggable" draggable="true">
      Drag me!
    </div>

    <div id="droppable1" class="droppable">
      Drop here!
    </div>

    <script>
      var draggables = document.getElementsByClassName("draggable");

      for (var i = 0; i < draggables.length; i++) {
        draggables[i].addEventListener("dragstart", function (event) {
          event.dataTransfer.setData("text", event.target.id);
        });
      }

      var droppables = document.getElementsByClassName("droppable");

      for (var i = 0; i < droppables.length; i++) {
        droppables[i].addEventListener("dragover", function (event) {
          event.preventDefault();
          event.target.classList.add("drag-over");
        });

        droppables[i].addEventListener("dragleave", function (event) {
          event.target.classList.remove("drag-over");
        });

        droppables[i].addEventListener("drop", function (event) {
          var id = event.dataTransfer.getData("text");
          var draggableElement = document.getElementById(id);
          event.target.appendChild(draggableElement);
          event.target.classList.remove("drag-over");
          event.target.classList.add("dropped");
          setTimeout(function () {
            event.target.classList.remove("dropped");
          }, 1000);
        });
      }
    </script>
  </body>
</html>

That's it! This code should now allow you to drag the "Drag me!" element onto the "Drop here!" element and drop it inside it.

Summary

In this chapter, we considered the basic algorithm for drag and drop. The interface of drag and drop allows applications to apply the drag and drop features on different browsers. The user can select a draggable element with the mouse, drag it to a droppable element, dropping it by releasing the mouse button.

For websites, extensions and so on, there is an option of customizing, which elements can become draggable, the type of feedback they produce. Also, there are frameworks building architecture on it: for example, DragZone, Droppable, Draggable, and other classes. Most of them act similarly to what was described above.

Practice Your Knowledge

What are the prerequisites for implementing drag and drop functionality with Javascript?

Quiz Time: Test Your Skills!

Ready to challenge what you've learned? Dive into our interactive quizzes for a deeper understanding and a fun way to reinforce your knowledge.

Do you find this helpful?