Search code examples
jqueryjquery-uidrag-and-dropjquery-ui-draggablejquery-ui-droppable

Enable droppable element to accept draggables only when it is empty


In this jsFiddle I am getting one issue. Let me describe

When I drag any element contains "N" to other <td> is working good. But after that when I try to drag any element to it's original position is not working.

My expectation : When any element is moved to other place then old place must be allow to accept other element.

Following is the code:

$(function () {
  $(".dragDiv").draggable({
    create: function () {
        $(this).data('position', $(this).position())
    },
    revert: 'invalid',
    stop: function () {
        $(this).draggable('option', 'revert', 'invalid');
    }
  });
  $('.dragDiv').droppable({
    greedy: true,
    tolerance: 'touch',
    drop: function (event, ui) {
        ui.draggable.draggable('option', 'revert', true);
    }
  });
  $(".mainTable tr td").droppable({
    drop: function (event, ui) {
        console.log(JSON.stringify($(this).attr('id')));
        console.log(JSON.stringify(ui.draggable.attr("id")));
        snapToMiddle(ui.draggable, $(this));
    },
    accept: function () {
        return $(this).find("*").length == 0;
    }
  });
});
function snapToMiddle(dragger, target) {
  var topMove = target.position().top - dragger.data('position').top + (target.outerHeight(true) - dragger.outerHeight(true)) / 2;
  var leftMove = target.position().left - dragger.data('position').left + (target.outerWidth(true) - dragger.outerWidth(true)) / 2;
  dragger.animate({
    top: topMove,
    left: leftMove
  }, {
    duration: 100,
    easing: 'easeOutBack'
  });
}

Solution

  • The problem is that jQuery UI draggable widget does not actually append the element to droppable on drop, it just manipulates the CSS positioning properties, hence $(this).find("*").length == 0; will be false even if the item is dropped into another droppable.

    You can fix this by manually appending the draggable to droppable.

    Also, you can make use of the position() utility method to center the draggable relative to droppable with ease.


    $(function() {
      $(".dragDiv").draggable({
        revert: 'invalid'
      });
      $(".mainTable tr td").droppable({
        accept: function() {
          return $(this).find("*").length == 0;
        },
        drop: function(event, ui) {
          var $this = $(this);
          $this.append(ui.draggable.css({
            /* manually append the element
                    and reset positioning */
            top: 0,
            left: 0
          }));
          ui.draggable.position({
            my: "center",
            at: "center",
            of: $this,
            using: function(pos) {
              $(this).animate(pos, 100, "easeOutBack");
            }
          });
        }
      });
    });
    .mainTable {
      margin: 20px auto;
      padding: 0px;
    }
    .mainTable tr td {
      width: 100px;
      height: 100px;
    }
    .dragDiv {
      text-align: center;
      background-color: #00ABA9;
      height: 50px;
      vertical-align: middle;
      margin: auto;
      position: relative;
      width: 50%;
    }
    <link href="http://code.jquery.com/ui/1.11.2/themes/smoothness/jquery-ui.css" rel="stylesheet" />
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script src="http://code.jquery.com/ui/1.11.2/jquery-ui.js"></script>
    <table border="1" class="mainTable">
      <tr>
        <td id="11">
          <div class="dragDiv" id="1">N</div>
        </td>
        <td id="12">&nbsp;</td>
        <td id="13">&nbsp;</td>
      </tr>
      <tr>
        <td id="21">&nbsp;</td>
        <td id="22">
          <div class="dragDiv" id="2">N</div>
        </td>
        <td id="23">&nbsp;</td>
      </tr>
      <tr>
        <td id="31">&nbsp;</td>
        <td id="32">&nbsp;</td>
        <td id="33">
          <div class="dragDiv" id="3">N</div>
        </td>
      </tr>
    </table>