Light-weight Drag & Drop using Javascript

In the pursuit of making rich and interactive web pages, we need ways to make the UI (user interface) easy and intuitive for the user. This improves the UX (user experience) factor.

One of the interaction is drag and drop. There are different ways to implement this functionality. Many libraries provide this functionality as plug and play.

Let’s see how we can implement this functionality from scratch using bare javascript, without any library.

Understanding Drag & Drop:

  1. When a user presses the left mouse button on an element, the element should be considered to be in a state that drag is started.
  2. Then when the mouse is moved, without releasing the mouse button, the element should be moved along (dragged) with the mouse cursor.
  3. When the mouse button is released, the element should be placed at that location (dropped).
For implementing this using javascript, we need 3 events to be handled: onmousedown, onmousemove and onmouseup. Hence we need 3 functions to handle the drag and drop feature.
However the onmousemove and onmouseup events should be handled only when the mouse is down.

Method 1

One way is to create functions for handling each of the events seperately.
In this case we would need to have a global flag to identify the state of mousedown, another one to determine on which element the event is triggered, in case we have multiple draggable elements.
Also we would need to have a following list of event attachment for each object.
object.addEventListener("mousedown",startDrag)
object.addEventListener("mousemove",drag) 
object.addEventListener("mouseup",drop)
OR
object.onmousedown = function(){startDrag()};
object.onmousemove = function(){drag()};
object.onmouseup = function(){drop()};
OR
<element onmousedown="startDrag(event)" onmousemove="drag(event)" onmouseup="drop(event)"> ... 
Drawbacks:
The above method just bloats the dom with all the event listeners added to the objects. Even though, at a point in time, we only need to handle movement/dragging of single object, the even listeners are present on all the other objects.
If there is a hierarchy of elements having some events attached, the bubbling of events need to be handled properly.

Method 2:

Lets take a closer look at the attached events. Do we need to attach all the 3 events to the object every time?
We need to have the mousemove and mouseup events to work only when the object is being dragged, i.e. the mousedown has occured on the object.
After mouseup we no longer need the mousemove and mouseup events, until the next mousedown on the object. 
Hence we can just attach the mousedown event on the object beforehand and attach the other events dynamically only when needed and discard them once done (object is dropped).
This reduces the static event listeners by a factor of 3.
startdrag = function(e) {
  let currElm = e.currentTarget;
  currElm.style.position = "absolute";
  var refX = e.clientX - currElm.offsetLeft; 
  var refY = e.clientY - currElm.offsetTop;
  
  currElm.onmousemove = (e) => {
   currElm.style.top = e.clientY - refY + "px";
   currElm.style.left = e.clientX - refX + "px";
  }
 
 currElm.onmouseup = (e) => {
  currElm.onmousemove = null; //detach the events
  currElm.onmouseup = null;
 }
}
refX and refY store the current position of the element, as we need to move relative to this position. Even though we move to the position of the mouse pointer, the element that is being  dragged might be big, and if we do not refer the offsets, then there would be a ‘snap’  effect. Try removing refX and refY to see the difference.
We can add the following within the functions, for further control in case of hierarchy.
e.preventDefault(); // to prevent default action, in case of button or link
e.stopPropagation(); // stop event from bubbling up

TLDR;

Full source code with test html is at:  javascriptDemos github
In the github source, I’ve added named functions instead of anonymous functions as described above.
Working demo is at: live demo

 

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s