在相对定位的容器之间移动可拖动元素

Moving draggable element between relatively positioned containers

我有一个页面,其中包含可以放入各种容器中的物品。我必须在任何给定时间将可拖动项添加到当前结束的容器中,以便根据 css 以某种方式设置样式,具体取决于可拖动项和放置目标的组合 class.在 javascript 中应用此样式不是一个选项 - 样式必须来自 CSS.

问题来了,因为我在可拖动对象前面添加的容器具有 position: relative 属性。当我移动它时,这会弄乱可拖动助手相对于鼠标的位置。

这是一个非常简化的问题演示:

$('.draggable').draggable({
  drag: _onDrag,
  helper: function () {
    return $(this).clone().prependTo('#div1');
  }
});

function _onDrag(event, ui) {
  ui.helper.hide();

  var elementAtPoint = $(document.elementFromPoint(event.clientX, event.clientY));

  if(elementAtPoint.hasClass('dropTarget')) {
   ui.helper.prependTo(elementAtPoint);
  }  
  
  ui.helper.show();
}
.draggable {
 display: inline-block;
 background: red;
 height:50px;
 width: 50px;
}

#div1 .item1 {
  background: green;
}

#div1 .item2 {
  background: purple;
}

#div2 .item1 {
  background: blue;
}

#div2 .item2 {
  background: orange;  
}

#div1, #div2 {
  position: relative;
  width: 100px;
  height: 100px;
  border: 1px solid #000;
  display: inline-block;
}
<script src="https://code.jquery.com/jquery-2.2.2.min.js"></script>
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.min.js"></script>

<div class="draggable item1">
  Drag me!
</div>

<div class="draggable item2">
  Drag me too!
</div>

<div id="div1" class="dropTarget">
  Drag the box here first...
</div>
<div id="div2" class="dropTarget">
  Then drag it here :(
</div>

在不更改 css 或 html 的情况下,我可以做些什么来防止可拖动对象失去它的位置?

发生这种情况是因为在重新排列DOM时,渲染位置会根据新的偏移量重新计算。修复它的最简单方法是不重新排列 DOM。您是否考虑过使用 droppable 小部件?它们提供了您正在寻找的几乎开箱即用的功能:

$('#draggable').draggable({
  helper: function () {
    return $(this).clone().prependTo('#div1');
  }
});
$('#div1, #div2').droppable({
  over: function(e, ui) {
    ui.helper.addClass($(this).data('target-class'));
  },
  out: function(e, ui) {
    ui.helper.removeClass($(this).data('target-class'));
  }
});
#draggable {
 display: inline-block;
 background: red;
 height:50px;
 width: 50px;
}

#draggable.green {
  background: green;
}

#draggable.blue {
  background: blue;
}

#div1, #div2 {
  position: relative;
  width: 100px;
  height: 100px;
  border: 1px solid #000;
  display: inline-block;
}
<script src="https://code.jquery.com/jquery-2.2.2.min.js"></script>
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.min.js"></script>

<div id="draggable">
  Drag me!
</div>

<div id="div1" class="dropTarget" data-target-class="green">
  Drag the box here first...
</div>
<div id="div2" class="dropTarget" data-target-class="blue">
  Then drag it here :)
</div>

更新:为了在不进行任何 CSS 更改的情况下执行此操作,您可以获得容器位置之间的差异,并将其用作位置偏移量。

$('.draggable').draggable({
  start: _onStart,
  drag: _onDrag,
  helper: function () {
    return $(this).clone().prependTo('#div1');
  }
});

function _onStart(event, ui) {
  this.initialContainer = ui.helper.parent();
  this.currentContainer = ui.helper.parent();
  this.topOffset = 0;
  this.leftOffset = 0;
}

function _onDrag(event, ui) {
  ui.helper.hide();

  var elementAtPoint = $(document.elementFromPoint(event.clientX, event.clientY));

  if(elementAtPoint.hasClass('dropTarget') &&
     !elementAtPoint.is(this.currentContainer)) {

    this.topOffset = elementAtPoint.position().top -
                     this.initialContainer.position().top;
    this.leftOffset = elementAtPoint.position().left -
                      this.initialContainer.position().left;

    this.currentContainer = elementAtPoint;

 ui.helper.prependTo(elementAtPoint);
  }
  
  ui.position.top -= this.topOffset;
  ui.position.left -= this.leftOffset;
  
  ui.helper.show();
}
.draggable {
 display: inline-block;
 background: red;
 height:50px;
 width: 50px;
}

#div1 .item1 {
  background: green;
}

#div1 .item2 {
  background: purple;
}

#div2 .item1 {
  background: blue;
}

#div2 .item2 {
  background: orange;  
}

#div1, #div2 {
  position: relative;
  width: 100px;
  height: 100px;
  border: 1px solid #000;
  display: inline-block;
}
<script src="https://code.jquery.com/jquery-2.2.2.min.js"></script>
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.min.js"></script>

<div class="draggable item1">
  Drag me!
</div>

<div class="draggable item2">
  Drag me too!
</div>

<div id="div1" class="dropTarget">
  Drag the box here first...
</div>
<div id="div2" class="dropTarget">
  Then drag it here :)
</div>

我仍然建议尽可能重写 CSS。第一种方法可读性更好,也更不脆弱