【问题标题】:Moving draggable element between relatively positioned containers在相对定位的容器之间移动可拖动元素
【发布时间】:2016-03-23 09:56:13
【问题描述】:

我有一个页面,其中包含可以放入各种容器中的项目。我必须在任何给定时间将可拖动项目添加到它当前结束的容器中,以便根据 css 以某种方式对其进行样式设置,这取决于可拖动项目和放置目标类的组合。不能在 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 的情况下,我可以做些什么来防止可拖动对象丢失其位置?

【问题讨论】:

    标签: html css jquery-ui draggable


    【解决方案1】:

    发生这种情况是因为在重新排列 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。第一种方法更具可读性且不那么脆弱

    【讨论】:

    • 感谢您的浏览。这种方法的问题是我真的不想将一个类应用到可拖动区域 - 可放置区域的类将取决于许多因素,我需要根据它结束的可放置来设置可拖动的样式,不是根据 div1 = green 等简单规则。根据页面加载的上下文,可拖放框可能有 20 个不同的类中的任何一个,并且根据编码在css.
    • 至于 ID,我很欣赏这是一个坏主意,我并没有在我的实际代码中这样做,我只是很快将示例放在一起,并没有真正考虑到我正在使用的选择器.
    • 为了澄清一点,假设这里有两个可拖动对象 - 其中一个需要完全按照您的演示工作。第二个,具有“secondDraggable”类,需要在一个盒子里变成紫色,在另一个盒子里变成橙色。 “firstDraggable”和“secondDraggable”在其放置空间内正确显示的规则已经存在于 css 中,我只需要在拖动框时应用它们。
    • 我更新了问题中的 sn-p,以便为您和/或任何未来的观众澄清。
    • 好吧,要这样做,您必须使用容器之间的差异作为偏移量手动重新计算助手的位置。我强烈建议您重写 CSS —— 它不仅可以让这一切变得更容易,而且让未来的更新更容易
    猜你喜欢
    • 1970-01-01
    • 2014-03-05
    • 2020-06-29
    • 1970-01-01
    • 2011-05-07
    • 1970-01-01
    • 2020-10-10
    • 1970-01-01
    • 2012-12-18
    相关资源
    最近更新 更多