如何为连接的sortables修复错误定位的可拖动助手(部分由浮动/相对定位的父元素引起)?

ndm*_*ndm 19 css jquery-ui jquery-ui-sortable jquery-ui-draggable twitter-bootstrap-3

前言

当我使用draggables + sortables放置在浮动的,相对定位的父元素时,我遇到了一个问题,即可拖动的助手被错误地偏移了.浮动父元素是Bootstrap列,其中多个可排序列表放在一列中,而可拖动列表放在另一列中.

这是一个工作示例代码段

$('.sortable').sortable({
  connectWith: '.sortable',
  revert: 600,
  forcePlaceholderSize: true,
  placeholder: 'ui-sortable-placeholder',
  tolerance: 'pointer'
}).disableSelection();

$('.draggable').draggable({
  connectToSortable: '.sortable',
  helper: 'clone',
  revert: true
}).disableSelection();
Run Code Online (Sandbox Code Playgroud)
.sortable-container {
  display: inline-block;
  width: 100px;
  vertical-align: top;
}
.sortable {
  cursor: move;
  margin: 0;
  padding: 0;
  list-style-type: none;
  vertical-align: top;
  border: 1px solid #000;
}
.ui-sortable-placeholder {
  background: #ff0000;
}
#draggables {
  margin: 0;
  padding: 0;
  list-style-type: none;
}
.draggable {
  margin: 4px;
  cursor: move;
  color: #fff;
  background: #5dd1ff;
}
Run Code Online (Sandbox Code Playgroud)
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.11.3/jquery-ui.min.js"></script>

<div class='container-fluid'>
  <div class="row">
    <div class="col-xs-3">
      <p>foo</p>
      <p>bar</p>
    </div>
    <div class="col-xs-3">
      <p>foo</p>
      <p>bar</p>
    </div>
    <div class="col-xs-6">
      <p>foo</p>
      <p>bar</p>
    </div>
  </div>
  <div class='row'>
    <div class='col-xs-6'>
      <div id='sortables'>
        <div class='sortable-container'>
          <ul class='sortable'>
            <li>sortable 1</li>
            <li>sortable 2</li>
            <li>sortable 3</li>
            <li>sortable 4</li>
            <li>sortable 5</li>
            <li>sortable 6</li>
          </ul>
        </div>
        <div class='sortable-container'>
          <ul class='sortable'>
            <li>sortable 1</li>
            <li>sortable 2</li>
            <li>sortable 3</li>
            <li>sortable 4</li>
            <li>sortable 5</li>
            <li>sortable 6</li>
          </ul>
        </div>
        <div class='sortable-container'>
          <ul class='sortable'>
            <li>sortable 1</li>
            <li>sortable 2</li>
            <li>sortable 3</li>
            <li>sortable 4</li>
            <li>sortable 5</li>
            <li>sortable 6</li>
          </ul>
        </div>
      </div>
    </div>

    <div class='col-xs-6'>
      <ul id='draggables'>
        <li class='draggable'>draggable 1</li>
        <li class='draggable'>draggable 2</li>
        <li class='draggable'>draggable 3</li>
      </ul>
    </div>
  </div>
</div>
Run Code Online (Sandbox Code Playgroud)

更新2015年11月16日我修改了代码示例以更好地反映我的实际使用情况,其中有更多行位于包含draggables/sortables的行之上.

一个截屏视频和一个显示发生了什么的静止图像

在此输入图像描述

进一步说明

当从右侧列中的一个可拖动列表拖动其中一个可拖动列表而不是丢弃它,但是将其拖出可排序列表边界框时,帮助程序定位不正确,它会移动一个左边几百个像素,好像它错误地结合了某种偏移(看起来它可能是原始的可拖动位置).

有趣的是,当draggables放置在与sortables相同的父元素中时,这不会发生,至少它不会水平移动,而是在可拖动快速上/下或左/右移入/移出可排序列表时垂直移动.

水平移位以某种方式与浮动/相对定位的父元素相关,禁用浮动或相对定位修复它.但是,我想保持原样,并找到另一个修复/解决方法.当不涉及浮动/相对定位时也会发生垂直移位,所以我想这个问题还有一些问题.

201511月15日更新 - jQuery UI更改

看起来jQuery UI 1.11.4稍微改变了行为,现在当你离开可排序的边界框时它不会立即水平移动,但你必须首先在两个或更多的可排序之间移动.除此之外,有缺陷的行为似乎没有改变.

201511月16日更新 - appendTo选项

最初我尝试使用appendTo一种解决方法,因为帮助器将保留在列表之外,虽然这对于我的原始示例代码是正确的,但它不适用于更新的示例代码,其中放置了更多行在持有draggables/sortables的那些之上,它们使助手垂直移动.

有没有人知道有问题的水平偏移源于何处,以及如何在保持使用浮动/相对定位的父元素的同时修复它?

那么垂直偏移,看到这也发生在jQuery UI演示中,让我觉得这是一个与父元素样式无关的错误?

201511月15日更新 - 找到垂直偏移问题

垂直偏移似乎与可拖动应用的边距有关,而不是它似乎工作正常.

我已经报告了两个错误,但我仍在寻找一个我可以自己应用的修复/解决方法,直到这可能会或可能不会在库中修复.

http://bugs.jqueryui.com/ticket/14822
http://bugs.jqueryui.com/ticket/14806

Jul*_*ire 6

奇怪的是,jquery-ui 10.4似乎更好用.不同之处在于,在10.4中,可拖动的助手保留在其原始div中,并被克隆到可排序但隐藏的中.因此计算更容易.

在11.4中,辅助函数被附加到被拖动的可排序对象上,这使得难以遵循精确的偏移计算.您经常需要更改父偏移量,并跟踪它的可排序性,可排序的位置,可排序的位置等等.显然那里有一个错误.

一个简单的解决方案是从10.4 获取connectToSortable插件.你必须检查不需要的副作用,但很快它似乎正在起作用.您可以使用其他名称,以便保留原始名称.像这样:

$.ui.plugin.add("draggable", "connectToSortable104", {
    // You take the whole connectToSortable plugin from  
    // here: https://code.jquery.com/ui/1.10.4/jquery-ui.js  
    // In 11.4 you'll need to add draggable parameter 
    // for example: to the event methods,
    // start: function(event, ui, draggable)
    ...
Run Code Online (Sandbox Code Playgroud)

http://jsfiddle.net/gsnojkbc/2/

编辑:

我不认为额外的div是导致问题的原因,它实际上是一个错误,因为connectToSortable在jquery 11.4中的工作方式导致了这个问题.为了允许在可排序中移动帮助程序并仍然跟踪正确的偏移量,每次帮助程序更改div时都需要重新调整一些数据.它的完成方式存在两个缺陷:

第一个是有一个refreshOffsets方法,对于draggable中的其他事件是通用的.例如,当您单击可拖动时,它就会被使用.因此它会尝试根据点击计算偏移量.但是当从可排序事件中调用refreshOffsets时,它会混淆单击偏移量.这可以通过更改refreshOffsets方法来解决, 以便不考虑event.pageX和Y.像这样:

$.ui.draggable.prototype._refreshOffsetsSortable = function(event, draggable){

        this.offset = {
                top: this.positionAbs.top - this.margins.top,
                left: this.positionAbs.left - this.margins.left,
                scroll: false,
                parent: this._getParentOffset(),
                relative: this._getRelativeOffset()
            };

            this.offset.click = draggable.offset.click;
    }
Run Code Online (Sandbox Code Playgroud)

出现另一个问题是因为您有许多可排序的问题.基本上,需要完成的其他操作是更改父偏移量.它现在的方式是它保存前一个父节点.通常它可以工作但是如果你移动得太快,序列会使得保存的父级是可排序的而不是原始的父级.您可以通过在拖动开始时保存父项来解决此问题,这在任何情况下都会使得更有意义.像这样:

$.ui.plugin.add( "draggable", "connectToSortableFixed", {
    start: function( event, ui, draggable ) {
        var uiSortable = $.extend( {}, ui, {
            item: draggable.element
        });
        draggable._parent = this.parent();
...
Run Code Online (Sandbox Code Playgroud)

见这里:http://jsfiddle.net/24a8q49j/1/


Mar*_*kus 5

一个简单的仅 JavaScript 解决方法是在拖动开始时存储鼠标的偏移量,然后强制 与ui.helper鼠标具有相同的偏移量 - 始终:

[...]
var offset_start = {};

$('.draggable').draggable({
  connectToSortable: '.sortable',
  helper: 'clone',
  revert: true,
  start: function(e, ui) {
      offset_start = {
          x: ui.position.left - e.pageX,
          y: ui.position.top - e.pageY
      }
  },
  drag: function(e, ui) {
      ui.position.top = e.pageY + offset_start.y
      ui.position.left = e.pageX + offset_start.x
  }
}).disableSelection();
Run Code Online (Sandbox Code Playgroud)

请参阅更新后的 jsfiddle

对于简单的情况,这是一个简单的解决方法,但如果引入边界(助手无法离开的地方)等,则需要更多工作。


编辑:对于您的代码片段,我想出了另一种解决方案(因为另一个解决方案无法按预期工作 - 获得初始偏移量比我想象的更难......):只需强制该项目停留在鼠标指针处,无论初始偏移量如何将助手附加到主体后的偏移量。它并不是 100% 好,因为当开始拖动时它可能会“跳”到鼠标 - 但随后它至少会留在那里......

$('.draggable').draggable({
  connectToSortable: '.sortable',
  helper: 'clone',
  revert: 'invalid',
  appendTo: 'body',
  drag: function(e, ui) {
    if (!ui.helper.parent().is('body')) {
      ui.helper.appendTo($('body'));
    }
    ui.position.top = e.pageY - 10;
    ui.position.left = e.pageX - 25;
  }
}).disableSelection();
Run Code Online (Sandbox Code Playgroud)

[...]
var offset_start = {};

$('.draggable').draggable({
  connectToSortable: '.sortable',
  helper: 'clone',
  revert: true,
  start: function(e, ui) {
      offset_start = {
          x: ui.position.left - e.pageX,
          y: ui.position.top - e.pageY
      }
  },
  drag: function(e, ui) {
      ui.position.top = e.pageY + offset_start.y
      ui.position.left = e.pageX + offset_start.x
  }
}).disableSelection();
Run Code Online (Sandbox Code Playgroud)
$('.draggable').draggable({
  connectToSortable: '.sortable',
  helper: 'clone',
  revert: 'invalid',
  appendTo: 'body',
  drag: function(e, ui) {
    if (!ui.helper.parent().is('body')) {
      ui.helper.appendTo($('body'));
    }
    ui.position.top = e.pageY - 10;
    ui.position.left = e.pageX - 25;
  }
}).disableSelection();
Run Code Online (Sandbox Code Playgroud)
$('.sortable').sortable({
  connectWith: '.sortable',
  revert: 600,
  forcePlaceholderSize: true,
  placeholder: 'ui-sortable-placeholder',
  tolerance: 'pointer'
}).disableSelection();


$('.draggable').draggable({
  connectToSortable: '.sortable',
  helper: 'clone',
  revert: 'invalid',
  appendTo: 'body',
  scroll: false,
  drag: function(e, ui) {
    if (!ui.helper.parent().is('body')) {
      ui.helper.appendTo($('body'));
    }
    ui.position.top = e.pageY - 15;
    ui.position.left = e.pageX - 25;
  }
}).disableSelection()
Run Code Online (Sandbox Code Playgroud)