前言
我遇到了一个问题,当使用放置在浮动的、相对定位的父元素中的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();
.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;
}
<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>
更新2015年11月16日我修改了代码示例,以更好地反映我的实际使用上下文,其中包含draggables/sortables的行上方还有更多行。
一个screencast和一个显示发生了什么的静止图像
进一步解释
当将右侧列中的一个可拖动对象拖到左侧的一个可排序列表上时,如果没有将其拖放,而是将其拖出可排序列表的边界框,则辅助对象的位置不正确,它向左移动了几百个像素,就好像它错误地包含了某种偏移量(看起来可能是原始的可拖动位置)。
有趣的是,当可拖放对象与可排序对象放置在同一父元素中时,这不会发生,至少它不会水平移动,而是当可拖放对象快速向上/向下或向左/向右移入/移出可排序列表时。
水平移动以某种方式与浮动/相对定位的父元素相关,禁用浮动或相对定位可以修复它。但是,我想保持原样,并找到另一个修复/解决方法。当不涉及浮动/相对定位时,垂直移位也会发生,所以我猜这个问题会更多一些。
更新2015年11月15日 - jQuery UI更改
看起来jQuery UI 1.11.4改变了一些行为,现在它不会在你离开可排序的边界框时立即水平移动,但你必须首先在两个或多个可排序之间移动。除此之外,be行为似乎没有变化。
更新2015年11月16日 - Update option
最初,我尝试使用appendTo
作为变通方法,因为帮助器将保留在列表之外,虽然这对我的原始示例代码是正确的,但它不适用于更新的示例代码,在更新的示例代码中,更多的行被放置在保存可拖动对象/可排序对象的行之上,它们导致帮助器垂直移动。
问题
有没有人知道有问题的水平偏移量到底来自哪里,以及如何在继续使用浮动/相对定位的父元素的同时修复它?
看到中也发生了垂直偏移,我认为这是一个与父元素的样式无关的错误。
更新2015年11月15日 -已找到垂直偏移问题
垂直偏移似乎与应用于可拖动对象的边距有关,没有这一点,它似乎工作得很好。
我已经报告了这两个错误,但我仍然在寻找一个修复/解决方法,我可以应用自己,直到这个问题可能在库中修复,也可能不会。
发布于 2015-11-17 04:13:12
奇怪的是,它似乎与jquery-UI10.4一起工作得更好。不同之处在于,在10.4中,可拖动帮助器保留在其原始div中,并被克隆到可排序中,但被隐藏。因此,计算更容易进行。
在11.4中,辅助对象被附加到它被拖动到的可排序表中,这使得精确的偏移量计算很难理解。您必须不断地更改父偏移量,并跟踪它是哪个可排序的、它是哪个可排序的、可排序的位置等等。很明显,这里面有个bug。
一个简单的解决方案是从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)
...
请参阅http://jsfiddle.net/gsnojkbc/2/
编辑:
我不认为额外的div是导致问题的原因,实际上是jQuery11.4中connectToSortable工作方式的错误导致了问题。要允许在排序中移动辅助对象,并且仍然跟踪适当的偏移量,您需要在每次辅助对象更改div时重新调整一些数据。它的实现方式有两个缺陷:
第一个是在draggable中有一个与其他事件通用的refreshOffsets方法。例如,当您单击可拖动对象时,就会使用它。因此它会尝试根据点击来计算偏移量。但是当从可排序事件调用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;
}
另一个问题发生是因为您有许多可排序。基本上,需要完成的另一个操作是更改父偏移量。现在的方法是保存之前的父对象。通常情况下,它可以工作,但如果移动太快,序列会使它成为保存的父对象是可排序的,而不是原始的父对象。您可以通过在拖动开始时保存父对象来修复此问题,这样做在任何情况下都更有意义。如下所示:
$.ui.plugin.add( "draggable", "connectToSortableFixed", {
start: function( event, ui, draggable ) {
var uiSortable = $.extend( {}, ui, {
item: draggable.element
});
draggable._parent = this.parent();
...
发布于 2015-11-21 22:42:38
一种仅限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();
请参阅updated 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();
$('.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()
.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;
}
<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>
发布于 2015-11-16 21:35:32
请参阅https://jsfiddle.net/tsLcjw9c/1/
我在draggables列表上设置了一个固定的宽度(100px),以便可拖动的辅助对象具有与列表项相同的宽度,这可以防止初始的水平偏移。
在可排序容器上拖动时得到的大水平偏移确实似乎是jquery UI中的一个错误,但它似乎恰好是可排序容器宽度的100%,这就是我添加以下css的原因:
.sortable .ui-draggable-dragging:not(.ui-sortable-helper){
margin-left:100%;
}
进入和离开可排序列表后得到的垂直偏移量似乎是由可拖动项的4px边距引起的,我删除了它。你可以使用透明边框和框大小来达到相同的效果:边框;或者添加填充并将背景剪辑设置为padding。
最后,你想要的600ms动画在我的浏览器(Chrome)中以一种奇怪的方式发生,它去了2个不同的位置,最后动画到了正确的位置。这就是我现在禁用动画的原因。您可以尝试覆盖可排序jQuery中的drop事件,以便手动设置指向正确位置的动画。
下面是jsfiddle代码:
html
<div class='container-fluid'>
<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>
jquery
$('.sortable').sortable({
connectWith: '.sortable',
revert: 0,
forcePlaceholderSize: true,
placeholder: 'ui-sortable-placeholder',
tolerance: 'pointer'
}).disableSelection();
$('.draggable').draggable({
connectToSortable: '.sortable',
helper: 'clone',
revert: false
}).disableSelection();
css
.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 {
cursor: move;
color: #fff;
background: #5dd1ff;
width:100px;
max-width:100%;
}
.sortable .ui-draggable-dragging:not(.ui-sortable-helper){
margin-left:100%;
}
https://stackoverflow.com/questions/33683926
复制相似问题