Ganesh Yadav
Ganesh Yadav

Reputation: 2685

JQueryUI - droppable only one at a time

I'm using jQueryUI for draggable and droppable.

If I drop one element in droppable area and again if try to drop 2nd object. First dropped element should automatically remove and moved to draggable area.

Basically, only one element should be in droppable area.

Here fiddle demo

enter image description here

As soon as droppable area has more than 1 element. It should just remove last element. If added one more element. Droppable area can't have more than 1 object.

code:

$("#dvSource img").draggable({
    revert: "invalid",
    //refreshPositions: true,
    drag: function (event, ui) {
        ui.helper.addClass("draggable");
    },
    stop: function (event, ui) {
        ui.helper.removeClass("draggable");
        if ($.ui.ddmanager.drop(ui.helper.data("draggable"), event)) {
                console.log($("#dvDest img").length);   
        }
        else {
            //alert(image + " not dropped.");
        }
    }
});
$("#dvDest").droppable({
    drop: function (event, ui) {
        if ($("#dvDest img").length == 0) {
            $("#dvDest").html("");
        }
        ui.draggable.addClass("dropped");
                $("#dvDest").append(ui.draggable);
    }
});

Upvotes: 1

Views: 1920

Answers (1)

Twisty
Twisty

Reputation: 30903

I'm not fabulous with animations, but here is one way to do it.

Working Example: https://jsfiddle.net/Twisty/6auhhjxc/5/

jQuery

$(function() {
  $("#dvSource img").draggable({
    revert: "invalid",
    drag: function(event, ui) {
      ui.helper.addClass("draggable");
    },
    stop: function(event, ui) {
      ui.helper.removeClass("draggable");
      if ($.ui.ddmanager.drop(ui.helper.data("draggable"), event)) {
        console.log($("#dvDest img").length);
      }
    }
  });
  $("#dvDest").droppable({
    drop: function(event, ui) {
      if ($("#dvDest img").length === 0) {
        $("#dvDest").html("");
      } else {
        $("#dvDest img:first").hide(600, function() {
          $(this).appendTo("#dvSource");
        }).show(600);
      }
      ui.draggable.addClass("dropped");
      $("#dvDest").append(ui.draggable);
    }
  });
});

In the drop, you already had a good check. So if there are img elements in the destination when you drop, You will want to append the drop and then append the first element back to the source.

I added some simple .hide() and .show() animations to make it fancy. I also adjusted each image, so you can see that the right item is moving back.

I suspect there is way to animate the the return in some fashion, that's just not in my bag of tricks. But this should get you there if that is in your bag.

Update after Comments

Working Example: https://jsfiddle.net/Twisty/6auhhjxc/7/

I found an issue in your draggable code that kept throwing an error. I removed it.

Since we destroy/create draggables over and over, I made a function to help do this. The drag/drop adds a bunch of styling to retain positioning so I drop that and the class when I pass the img back to the source.

jQuery

function makeDrag(el) {
  // Pass me an object, and I will make it draggable
  el.draggable({
    revert: "invalid"
  });
}
$(function() {

  makeDrag($("#dvSource img"));

  $("#dvDest").droppable({
    drop: function(event, ui) {
      if ($("#dvDest img").length === 0) {
        $("#dvDest").html("");
      } else {
        $("#dvDest img:first").hide(600, function() {
          $(this).removeAttr("class");
          $(this).removeAttr("style");
          $(this).appendTo("#dvSource");
        }).show(600, function() {
          makeDrag($(this));
        });
      }
      ui.draggable.addClass("dropped");
      ui.draggable.draggable("destroy");
      $("#dvDest").append(ui.draggable);
    }
  });
});

The code in your stop that was causing an issue:

if ($.ui.ddmanager.drop(ui.helper.data("draggable"), event)) {
    console.log($("#dvDest img").length);
}

I switched to the uncompressed version, and the error thrown was:

TypeError: draggable is undefined

$.each( ( $.ui.ddmanager.droppables[ draggable.options.scope ] || [] ).slice(), ...

The section of code for this is:

    $.each( ( $.ui.ddmanager.droppables[ draggable.options.scope ] || [] ).slice(), function() {

        if ( !this.options ) {
            return;
        }
        if ( !this.options.disabled && this.visible && $.ui.intersect( draggable, this, this.options.tolerance, event ) ) {
            dropped = this._drop.call( this, event ) || dropped;
        }

        if ( !this.options.disabled && this.visible && this.accept.call( this.element[ 0 ], ( draggable.currentItem || draggable.element ) ) ) {
            this.isout = true;
            this.isover = false;
            this._deactivate.call( this, event );
        }

    });

In the minimized code, this was t, but the issue is the same. When you passed ui.helper.data("draggable") to $.ui.ddmanager.drop(), the value was null or undefined. The reason for this, in the scope of your script, was that ui.helper did not have a data-draggable attribute.

Again, it was not clear what the intended action was there, so I simply removed it.

Upvotes: 2

Related Questions