Simulate Drag and Drop with JavaScript and CasperJS

We use a number of browser automation tools here at Ghost Inspector including CasperJS — which is a handy wrapper for controlling operations in PhantomJS and SlimerJS headless browsers.

Today I'm going to present some JavaScript code that can be used for simulating drag-and-drop events in a browser. This can be used as standalone code, or you can call it through CasperJS to simulate drag-and-drop events in your tests. I'll show you both. Let's take a look at the raw JavaScript function first:

var triggerDragAndDrop = function (selectorDrag, selectorDrop) {

  // function for triggering mouse events
  var fireMouseEvent = function (type, elem, centerX, centerY) {
    var evt = document.createEvent('MouseEvents');
    evt.initMouseEvent(type, true, true, window, 1, 1, 1, centerX, centerY, false, false, false, false, 0, elem);
    elem.dispatchEvent(evt);
  };

  // fetch target elements
  var elemDrag = document.querySelector(selectorDrag);
  var elemDrop = document.querySelector(selectorDrop);
  if (!elemDrag || !elemDrop) return false;

  // calculate positions
  var pos = elemDrag.getBoundingClientRect();
  var center1X = Math.floor((pos.left + pos.right) / 2);
  var center1Y = Math.floor((pos.top + pos.bottom) / 2);
  pos = elemDrop.getBoundingClientRect();
  var center2X = Math.floor((pos.left + pos.right) / 2);
  var center2Y = Math.floor((pos.top + pos.bottom) / 2);
  
  // mouse over dragged element and mousedown
  fireMouseEvent('mousemove', elemDrag, center1X, center1Y);
  fireMouseEvent('mouseenter', elemDrag, center1X, center1Y);
  fireMouseEvent('mouseover', elemDrag, center1X, center1Y);
  fireMouseEvent('mousedown', elemDrag, center1X, center1Y);

  // start dragging process over to drop target
  fireMouseEvent('dragstart', elemDrag, center1X, center1Y);
  fireMouseEvent('drag', elemDrag, center1X, center1Y);
  fireMouseEvent('mousemove', elemDrag, center1X, center1Y);
  fireMouseEvent('drag', elemDrag, center2X, center2Y);
  fireMouseEvent('mousemove', elemDrop, center2X, center2Y);
  
  // trigger dragging process on top of drop target
  fireMouseEvent('mouseenter', elemDrop, center2X, center2Y);
  fireMouseEvent('dragenter', elemDrop, center2X, center2Y);
  fireMouseEvent('mouseover', elemDrop, center2X, center2Y);
  fireMouseEvent('dragover', elemDrop, center2X, center2Y);
  
  // release dragged element on top of drop target
  fireMouseEvent('drop', elemDrop, center2X, center2Y);
  fireMouseEvent('dragend', elemDrag, center2X, center2Y);
  fireMouseEvent('mouseup', elemDrag, center2X, center2Y);

  return true;
};

You can see we've created a triggerDragAndDrop() function which accepts two CSS selector arguments for the drag and drop elements, respectively. Our function is essentially just triggering a long sequence of mouse and drag events to simulate the same events that are triggered when something is dragged manually. To simulate a drag-and-drop action, simply call this function and pass in the targets, like this: triggerDragAndDrop('.drag-item', '.drop-zone');.

If you'd like to use this during your CasperJS test, simply drop it into a casper.evaluate() call, like this:

casper.evaluate(function triggerDragAndDrop(selectorDrag, selectorDrop) {
[...]
}, '.drag-item', '.drop-zone');

That's it. It does not require jQuery or any other libraries. This snippet will work on its own for most drag-and-drop situations. Note that drag-and-drop libraries can be complex and sometimes look for different events. We've had good success with this code, but have found situations where it's unsuccessful. If the snippet above isn't working for you, additional events may need to be triggered.

We hope this code snippet is useful for you, and of course, feel free to try out Ghost Inspector's own drag-and-drop functionality by signing up for a free account today!
Justin Klemm

Author: Justin Klemm

Justin is the founder and tech lead at Ghost Inspector. He's a seasoned developer with a passion for innovation. When he's not tinkering with the latest web frameworks, Justin enjoys world traveling, good eats and lots of outdoor activity.

Web | Twitter | Google+