13

How do I programmatically abort a jQuery drag operation?

Using jQuery UI, the user begins a drag operation. While dragging, an asynchronous event occurs that causes the dragged element to be deleted (a timer-based refresh, for example). In the refresh, I would like to do something like this before the refresh to avoid errors from the deleted element :

   element.trigger('dragstop');

But that didn't seem to work.

remack
  • 303
  • 2
  • 7
  • 2
    Since the draggable currently lacks a sanctioned way to cancel on command, every answer will be a hack containing various degrees of unwanted side-effects. Here's the feature request ticket: http://bugs.jqueryui.com/ticket/8414 – Brennan Roberts Jun 28 '12 at 17:25

4 Answers4

7

This can be done by using the callback function (returning false) of the drag or drop event. See http://jqueryui.com/demos/draggable/#event-drag

FerRory
  • 71
  • 1
4

I add this answer as I was searching for the same problem and I wanted a short answer.

Just return false in the drag event.

Thank you to your long answer though as I found mine through them.

Nicolas Thery
  • 2,319
  • 4
  • 26
  • 36
  • This seems to work fine -- how come the bug report has a `wontfix` [1] - http://bugs.jqueryui.com/ticket/8414 – sebhaase May 29 '19 at 09:06
2

Officially cancelling a drag while dragging is not allowed in jQuery UI "by design"[1] -- I disagree with the situation, but it is as it is.

If you somewhat reliably want to cancel a drag mid-flight, you will need to combine a couple of hacks [2, 3]:

$( window ).keydown( function( e ){
  if( e.keyCode == 27 ) {
    var mouseMoveEvent = document.createEvent("MouseEvents");
    var offScreen = -50000;

    mouseMoveEvent.initMouseEvent(
      "mousemove", //event type : click, mousedown, mouseup, mouseover, etc.
      true, //canBubble
      false, //cancelable
      window, //event's AbstractView : should be window
      1, // detail : Event's mouse click count
      offScreen, // screenX
      offScreen, // screenY
      offScreen, // clientX
      offScreen, // clientY
      false, // ctrlKey
      false, // altKey
      false, // shiftKey
      false, // metaKey
      0, // button : 0 = click, 1 = middle button, 2 = right button
      null // relatedTarget : Only used with some event types (e.g. mouseover and mouseout).
            // In other cases, pass null.
    );

    // Move the mouse pointer off screen so it won't be hovering a droppable
    document.dispatchEvent(mouseMoveEvent);

    // This is jQuery speak for:
    // for all ui-draggable elements who have an active draggable plugin, that
    var dragged = $('.ui-draggable:data(draggable)')
      // either are ui-draggable-dragging, or, that have a helper that is ui-draggable-dragging
      .filter(function(){return $('.ui-draggable-dragging').is($(this).add(($(this).data('draggable') || {}).helper))});

    // We need to restore this later
    var origRevertDuration = dragged.draggable('option', 'revertDuration');
    var origRevertValue = dragged.draggable('option', 'revert');

    dragged
      // their drag is being reverted
      .draggable('option', 'revert', true)
      // no revert animation
      .draggable('option', 'revertDuration', 0)
      // and the drag is forcefully ended
      .trigger('mouseup')
      // restore revert animation settings
      .draggable('option', 'revertDuration', origRevertDuration)
      // restore revert value
      .draggable('option', 'revert', origRevertValue);
  }
}

Now, this isn't pretty. But it works. Even when canceling while hovering an accepting droppable.

Have fun with it.

[1] - http://bugs.jqueryui.com/ticket/8414
[2] - https://gist.github.com/3517765
[3] - https://forum.jquery.com/topic/how-to-cancel-drag-while-dragging

eleotlecram
  • 1,194
  • 12
  • 9
1

As @FerRory suggests, you could take advantage of the drag event.

You could take advantage of the data() method to determine whether an element is draggable or not:

$("div").draggable({
    drag: function() {
       return !$(this).data("disabledrag");
    },
    revert: true
});

Then in your async action, you could set that data if the element being dragged is the one that's been deleted (I'm assuming you have some system for associating DOM elements with data from the server):

var $dragging = $(".ui-draggable-dragging");
if ($dragging.length) {
    // If this is the item that was deleted, stop dragging:
    if ($dragging.attr("id") === "item-one") {
        $dragging.data("disabledrag", true);
        $dragging.addClass("deleted").html("This item has been deleted!");            
    }
}

I've updated my example here. The first div will be "deleted" after 5 seconds.

Community
  • 1
  • 1
Andrew Whitaker
  • 124,656
  • 32
  • 289
  • 307
  • That triggers the stop event, but it doesn't cancel the drag. If you modify your example drag to this you can see it when you drag the rectangle: stop: function() { $('div').css('background-color','green').css('border', 'solid 1px black'); }, drag: function() { $('div').css('border', '');} – remack Jan 28 '11 at 23:11
  • @remack: Okay, I misunderstood. Do you want to revert the draggable object back to its original position? – Andrew Whitaker Jan 28 '11 at 23:43