Drag and drop multiple selected draggables and revert invalid ones using Jquery UI

对着背影说爱祢 提交于 2019-11-26 23:10:52

问题


Drawing a box with cursor (lasso) will select multiple .item in this JSFiddle example.

Selected .item's become draggable. Empty .slot without an .item inside is a valid droppable.

When you drop multiple draggables on multiple droppables, only the .item which mouse is on will revert if its corresponding droppable not valid.

How to make every draggable revert if it's dropped on invalid droppable?

Javascript:

$(function () {
  // we are going to store the selected objects in here
  var selected = $([]),
    total = [],
    offset = {
        top: 0,
        left: 0
    };

  $(document).selectable({
    filter: ".item",
    start: function (event, ui) { //remove draggable from selection, otherwise previous selection will still be draggable. 
        $(total).draggable("destroy");
    },
    selected: function (event, ui) { // push selected into total[].
        total.push(ui.selected)
    },
    unselected: function (event, ui) { //console.log('unselect ui: ',ui)
        u = ui.unselected
        //remove unselected from selection[].
        total = jQuery.grep(total, function (n) {
            return n !== u
        });
        //console.log('total array (unselected): ',total)
    },
    stop: function (vent, ui) {
        //remove duplicated element from total[].
        jQuery.unique(total)
        $(total).each(function () {
            $(this).draggable(dragOption)
        })
        //$(total).draggable(dragOption);
        //var widget = $( ".selector" ).draggable( "widget" );
        //console.log('widget: ',widget)
        console.log('break line---------------------------- ')
    }

  });

  //save drag option as an Obj.
  dragOption = {
    opacity: 0.45,
    delay: 300,
    connectToSortable: ".slot"
    //,helper: "clone"
    ,
    distance: 5,
    snap: ".slot",
    snapMode: "inner",
    revert: "invalid",
    start: function (event, ui) {
        console.log('draggable start ui: ', ui)
        selected = $(total).each(function () {
            var el = $(this);
            el.data("offset", el.offset())
        });
        offset = $(this).offset(); //get coordinates relative to document
    },
    drag: function (event, ui) { //console.log(offset.top)
        var dt = ui.position.top - offset.top,
            dl = ui.position.left - offset.left;
        selected.not(this).each(function () {
            // create the variable for we don't need to keep calling $("this")
            // el = current element we are on
            // off = what position was this element at when it was selected, before drag
            var el = $(this),
                off = el.data("offset");
            el.css({
                top: off.top + dt,
                left: off.left + dl
            });
        });
    },
    stop: function (event, ui) {
        console.log('drag stop ui : ', ui)
    }
  };

  //save drop option as an Obj.
  dropOption = {
    accept: '.item',
    drop: function (event, ui) {
        console.log('drop event : ', event);
        console.log('drop ui : ', ui)
    },
    activate: function (event, ui) { //console.log('this : ',this,'\n ui : ',ui)
    },
    out: function (event, ui) { //console.log('out',$(this))
    },
    deactivate: function (event, ui) { //console.log('deactivate')
    },
    tolerance: "intersect",
    instance: function (event, ui) { //console.log('instance ui : ',ui)
    },
    over: function (event, ui) { //console.log('this item : ',ui.draggable[0],'on this slot: ',this)
    },
    activeClass: "green3"

}
  // make empty slot droppable
  $(".slot:not(:has(>div))").droppable(dropOption)
}) <!--doc ready-->

HTML:

<body>
  <div id="container">
    <div id="header"></div>
    <div class="box" id="boxA">
      <h4>box A</h4>
      <div class="slot" id="A1">
        <div class="item" id="a1"></div>
      </div>
      <div class="slot" id="A2">
        <div class="item" id="a2"></div>
      </div>
      <div class="slot" id="A3">
        <div class="item" id="a3"></div>
      </div>
      <div class="slot" id="A4"></div>
      <div class="slot" id="A5"></div>
    </div>
    <div class ="box" id="boxB">
      <h4>box B</h4>
      <div class="slot" id="B1"></div>
      <div class="slot" id="B2">
        <div class="item" id="b2"></div>
      </div>
      <div class="slot" id="B3"></div>
      <div class="slot" id="B4"></div>
      <div class="slot" id="B5">
        <div class="item" id="b5"></div>
      </div>
    </div>
  </div> 
</body>

CSS:

document {
    background-color: #FFF;
}

.box {
    height: 180px;
    float: left;
    border-top-width: 5px;
    border-right-width: 5px;
    border-bottom-width: 5px;
    border-left-width: 5px;
    border-top-style: solid;
    border-right-style: solid;
    border-bottom-style: solid;
    border-left-style: solid;
    border-top-color: #999;
    border-right-color: #999;
    border-bottom-color: #999;
    border-left-color: #999;
    width: 150px;
    text-align: center;
    margin-left: 100px;
}
.item {
    position: absolute;
    font-size: 12px;
    height: 14px;
    background-color: #CCC;
    width: 110px;
    text-decoration: none;
    font-family: Arial, Helvetica, sans-serif;
    color: #999;
    margin-left: 6px;
    text-align: center;
}

#header, #footer {
    float: left;
    height: 200px;
    width: 100%;
}
.slot{
    border-top-width: 1px;
    border-right-width: 1px;
    border-bottom-width: 1px;
    border-left-width: 1px;
    border-top-style: dotted;
    border-right-style: dotted;
    border-bottom-style: dotted;
    border-left-style: dotted;
    height: 15px;
    width: 120px;
    margin-top: 2px;
    text-align: center;
    vertical-align: middle;
    line-height: 90px;
    margin-right: auto;
    margin-left: auto;
    }
.ui-selecting {
    background: #FECA40;
  }
.ui-selected {
    background-color: #F90;
  }
.green3 {
    background-color: #D9FFE2;
}

回答1:


You can make use of document.elementFromPoint.

Updated Fiddle

For dropping/reverting the original draggable, we do the following once it is dropped in drop event:

  1. Check whether the droppable contains an .item or not in the dropevent. If the droppable doesn't already contain an .item, Go to step 2, else go to step3
  2. append() the element to corresponding .slot after resetting the positioning using css()
  3. Revert the element using animate() method to previous position which we cached using .data() on start event.

For dropping/reverting the extra items being dragged:

  • We find the elements on the left and right visible area of each item being dragged (1px far from the element being dragged, Which is supposed to be the drop targets) usingdocument.elementFromPoint.

  • Since no actual drop event is triggered for these elements, we make use of the stop event of draggable to check whether either of the target's is a .slot

  • If either of them is a .slot, which means the item was dropped over a droppable, we proceed to step1 mentioned above, else we go to step3 (manually revert the item to it's original position)


$(function() {

  var dragOption = {
      delay: 10,
      distance: 5,
      opacity: 0.45,
      revert: "invalid",
      revertDuration: 100,
      start: function(event, ui) {
        $(".ui-selected").each(function() {
          $(this).data("original", $(this).position());
        });
      },
      drag: function(event, ui) {
        var offset = ui.position;
        $(".ui-selected").not(this).each(function() {
          var current = $(this).offset(),
            targetLeft = document.elementFromPoint(current.left - 1, current.top),
            targetRight = document.elementFromPoint(current.left + $(this).width() + 1, current.top);
          $(this).css({
            position: "relative",
            left: offset.left,
            top: offset.top
          }).data("target", $.unique([targetLeft, targetRight]));
        });
      },
      stop: function(event, ui) {
        $(".ui-selected").not(this).each(function() {
          var $target = $($(this).data("target")).filter(function(i, elm) {
            return $(this).is(".slot") && !$(this).has(".item").length;
          });
          if ($target.length) {
            $target.append($(this).css({
              top: 0,
              left: 0
            }))
          } else {
            $(this).animate({
              top: 0,
              left: 0
            }, "slow");
          }

        });
        $(".ui-selected").data("original", null)
          .data("target", null)
          .removeClass("ui-selected");
      }
    },
    dropOption = {
      accept: '.item',
      activeClass: "green3",
      drop: function(event, ui) {
        if ($(this).is(".slot") && !$(this).has(".item").length) {
          $(this).append(ui.draggable.css({
            top: 0,
            left: 0
          }));
        } else {
          ui.draggable.animate({
            top: 0,
            left: 0
          }, 50);
        }
      }
    }

  $(".box").selectable({
    filter: ".item",
    start: function(event, ui) {
      $(".ui-draggable").draggable("destroy");
    },
    stop: function(event, ui) {
      $(".ui-selected").draggable(dragOption)
    }
  });
  $(".slot").droppable(dropOption);
});
.box {
  float: left;
  width: 150px;
  height: 180px;
  text-align: center;
  margin-left: 20px;
  border: 5px solid #999;
}
.slot {
  position: relative;
  width: 120px;
  height: 15px;
  margin-top: 2px;
  margin: 0 auto;
  border: 1px dotted;
}
.item {
  width: 110px;
  height: 14px;
  margin: 0 auto;
  z-index: 1;
  background-color: #CCC;
}
.ui-selecting {
  background: #FECA40;
}
.ui-selected {
  background-color: #F90;
}
.green3 {
  background-color: #D9FFE2;
}
<link href="http://code.jquery.com/ui/1.9.2/themes/base/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.9.2/jquery-ui.js"></script>
<div id="container">
  <div id="boxA" class="box">
    <h4>box A</h4>

    <div id="A1" class="slot">
      <div id="a1" class="item"></div>
    </div>
    <div id="A2" class="slot">
      <div id="a2" class="item"></div>
    </div>
    <div id="A3" class="slot">
      <div id="a3" class="item"></div>
    </div>
    <div id="A4" class="slot"></div>
    <div id="A5" class="slot"></div>
  </div>
  <div id="boxB" class="box">
    <h4>box B</h4>

    <div id="B1" class="slot"></div>
    <div id="B2" class="slot">
      <div id="b2" class="item"></div>
    </div>
    <div id="B3" class="slot"></div>
    <div id="B4" class="slot"></div>
    <div id="B5" class="slot">
      <div id="b5" class="item"></div>
    </div>
  </div>
</div>

Other references:

  • jQuery.unique()
  • is()
  • has()

P.S: This will only work if the draggable is smaller than droppable (Which is true in this case)




回答2:


If you mean, you want to drag and drop something and you want to revert if the data is dropped in the wrong area, then you can you use following code as a sample to achive your target

$(function() {
  $(".connectedSortable").sortable({
    connectWith: ".connectedSortable",
    placeholder: "ui-state-highlight",
    //containment: "parent",
    revert: true
  });
  $("#sortable2").sortable({
    connectWith: ".connectedSortable",
    receive: function(event, ui) {
      if (ui.item.attr('cust-attr') != 'm') {
        console.log('wrong element');
        $("#" + ui.sender.attr('id')).sortable('cancel');
      }
    }
  });
  $("#sortable2x").sortable({
    connectWith: ".connectedSortable",
    receive: function(event, ui) {
      if (ui.item.attr('cust-attr') == 'm') {
        console.log('wrong element');
        $("#" + ui.sender.attr('id')).sortable('cancel');
      }
    }
  });
});
#sortable1,
#sortable1x,
#sortable2,
#sortable2x {
  list-style-type: none;
  margin: 0;
  padding: 0 0 2.5em;
  float: left;
  margin-right: 10px;
}
#sortable1 li,
#sortable1x li,
#sortable2 li,
#sortable2x li {
  box-shadow: 2px 2px 0 #6D6D6D;
  cursor: pointer;
  margin: 0 5px 5px 5px;
  padding: 5px;
  font-size: 1.2em;
  width: 150px;
}
#sortable2 .ui-selecting,
#sortable2x .ui-selecting {
  background: #FECA40;
}
#sortable2 .ui-selected,
#sortable2x .ui-selected {
  background: #F39814;
  color: white;
}
#sortable1x,
#sortable2x {
  list-style-type: none;
  margin: 0;
  width: 60%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://code.jquery.com/ui/1.11.2/jquery-ui.js"></script>
<link href="http://code.jquery.com/ui/1.11.2/themes/smoothness/jquery-ui.css" rel="stylesheet">
<div style="width:600px;height:360px;">
  <div style="float:left;">
    Set 1 : &nbsp;
  </div>
  <div>
    <ul id="sortable1" class="connectedSortable" style="border: 1px solid #000000;width:220px;height:300px;padding:4px;">
      <li class="ui-state-default" cust-attr="m">Item 1 Set 1 X</li>
      <li class="ui-state-default" cust-attr="m">Item 2 Set 1 X</li>
      <li class="ui-state-default">Item 3 Set 1 X</li>
      <li class="ui-state-default">Item 4 Set 1 X</li>
      <li class="ui-state-default">Item 5 Set 1 X</li>
    </ul>
    <ul id="sortable1x" class="connectedSortable" style="border: 1px solid #000000;width:220px;height:300px;padding:4px;">
      <li class="ui-state-default" cust-attr="m">Item 1 Set 1 Y</li>
      <li class="ui-state-default" cust-attr="m">Item 2 Set 1 Y</li>
      <li class="ui-state-default">Item 3 Set 1 Y</li>
      <li class="ui-state-default">Item 4 Set 1 Y</li>
      <li class="ui-state-default">Item 5 Set 1 Y</li>
    </ul>
  </div>
</div>
<div style="width:600px;height:360px;">
  <div style="float:left;">
    Set 2 : &nbsp;
  </div>
  <div>
    <ul id="sortable2" class="connectedSortable connectedSortablex" style="border: 1px solid #000000;width:220px;height:300px;padding:4px;">

    </ul>
    <ul id="sortable2x" class="connectedSortable connectedSortablex" style="border: 1px solid #000000;width:220px;height:300px;padding:4px;">

    </ul>
  </div>
</div>

Click the below link to see demo http://jsfiddle.net/g90rau5p/3/

If you want multiple items drop, use multisort jquery library which can be used along with jqueryUI. For more details about it go to https://github.com/shvetsgroup/jquery.multisortable



来源:https://stackoverflow.com/questions/26823175/drag-and-drop-multiple-selected-draggables-and-revert-invalid-ones-using-jquery

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!