Search code examples
javascriptjqueryjquery-uijquery-ui-draggablejquery-ui-droppable

Draggable drops in wrong place on second drag


I'm trying to drag "templates" (initially as clones) from a "store" into droppable slots. Thereafter I want to be able to drop the dragged item into whatever slot I wish. Draggables are set as follows:

$( ".template" ).draggable({
        helper: "clone",
    }
);

$( ".item" ).draggable();

I got round the issue of the thing re-cloning by replacing the "template" class with an "item" class on first drop and within the drop function switching from a clone to a straight element as follows:

$( ".template-slot" ).droppable({ 
accept: ".template, .item",

drop: function( event, ui ) {

    if(ui.draggable.hasClass("template")) {

        $(this).append(ui.draggable.clone())

        // add item class 
        $(".template-slot .template").addClass("item");

        // remove template class
        $(".item").removeClass("ui-draggable template");

        // expand to fit the slot
        $(".item").addClass("expand-to-slot");

        // make it draggable again
        $(".item").draggable();    

    }

    if(ui.draggable.hasClass("item")) {
        $(this).append(ui.draggable);
    }

}                            
});

The first drag and drop works okay. the problem arises on the second drag and drop. The element drags okay but when dropped it gets placed twice as far in whatever direction I drag it. So, for example, if I've dragged it 50px to the right, it drops 100px to the right. the same with up and down etc.

I suspect it's something to do with the offset being cumulative but haven't worked out why it's doing this or how to fix it.

I've created a fiddle here: https://jsfiddle.net/stefem1969/a86jmnxu/10/ to show the problem.

Hope someone can help.


Solution

  • Working Example: https://jsfiddle.net/Twisty/fu83zq2y/11/

    JavaScript

    $(function() {
    
      function makeDrag(obj, opt) {
        if (obj.hasClass("ui-draggable")) {
          obj.removeClass("ui-draggable");
          obj.attr("style", "");
        }
        if (opt === undefined) {
          opt = {};
        }
        obj.draggable(opt);
      }
    
      makeDrag($(".template, .item"), {
        helper: "clone",
      });
    
      $(".template-slot").droppable({
        accept: ".template, .item",
        drop: function(event, ui) {
          if (ui.draggable.hasClass("template")) {
            console.log("it has the template class!");
            var newItem = ui.draggable.clone();
            newItem.toggleClass("template item");
            newItem.addClass("expand-to-slot");
            newItem.appendTo($(this));
            makeDrag(newItem);
          }
    
          if (ui.draggable.hasClass("item")) {
            console.log("it has the item class!")
            var droppableOffset = $(this).offset();
            console.log("droppableOffset: ", droppableOffset);
            ui.draggable.attr("style", "").appendTo($(this));
          }
        }
      });
    
      $(".template-store, .thagomizer").droppable({
        accept: ".item",
        drop: function(event, ui) {
          ui.draggable.remove();
        }
      });
    });
    

    Part of draggable is to set a top and left when moving it around. Even if you append the item to another element, it will still have those. Clearing that can help accomplish what you're looking for.

    The makeDrag() is just helpful to perform the same operations a lot.

    Hope this helps.