Подтвердить что ты не робот

Как исправить ошибочно расположенные перетаскиваемые помощники для связанных сортировок (частично вызванные размещенными/относительными позиционированными родительскими элементами)?

Введение

У меня возникает проблема, когда перетаскиваемый помощник неправильно смещается при использовании 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="#" onclick="location.href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css'; return false;" 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>
4b9b3361

Ответ 1

Странно, что, похоже, он работает лучше с jquery-ui 10.4. Разница в том, что в 10.4 перетаскиваемый помощник остается в своем исходном div и клонируется в сортировки, но скрыт. Поэтому вычисления легче сделать.

В 11.4 вспомогательный добавляется к сортировке, по которой он перетаскивается, что затрудняет последующие точные вычисления смещения. Вам постоянно приходится менять родительское смещение и отслеживать, по какой сортировке он находится, по какой сортировке он был, положению сортируемого и так далее. И, очевидно, там есть ошибка.

Одним простым решением было бы получить плагин connectToSortable от 10.4. Вам придется проверять наличие нежелательных побочных эффектов, но быстро это работает. Вы можете использовать другое имя, чтобы сохранить оригинал. Вот так:

$.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/

EDIT:

Я не думаю, что дополнительный div является причиной проблемы, это действительно ошибка, связанная с тем, как connectToSortable работает в jquery 11.4, что вызывает проблему. Чтобы разрешить перемещение помощника в сортировках и по-прежнему отслеживать правильное смещение, вам нужно перенастроить некоторые данные каждый раз, когда помощник изменяет div. Там есть два недостатка:

Во-первых, существует метод 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;
    }

Другая проблема возникает, потому что у вас много сортировок. В основном  другая операция, которая должна быть выполнена, - это изменение родительского  смещение. То, как это делается прямо сейчас, - это сохранение предыдущего  Родитель. Обычно это работает, но если вы двигаетесь слишком быстро, последовательность  делает так, чтобы сохраненный родитель был сортируемым, а не оригиналом  Родитель. Вы можете исправить это, сохранив родителя при запуске drag, который в  в любом случае, кажется, имеет смысл. Вот так:

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

Смотрите здесь: http://jsfiddle.net/24a8q49j/1/

Ответ 2

Обходной путь простого 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();

Смотрите обновленный jsfiddle.

Для простого случая это простой способ обхода, но требует больше работы, если вы вводите границы (где помощник не может уйти) и т.д.


Edit: для вашего фрагмента я придумал другое решение (потому что другой работает не так, как ожидалось), сложнее получить начальные смещения, чем я думал...): просто заставляйте элемент оставаться у указателя мыши вне зависимости от того, начального смещения после добавления хелпера к телу. Это не на 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>

Ответ 3

см. https://jsfiddle.net/tsLcjw9c/1/

Я помещаю фиксированную ширину в список перетаскиваемых (100px), так что перетаскиваемый помощник имеет ту же ширину, что и элементы списка, это предотвращает начальное горизонтальное смещение.

большое горизонтальное смещение, которое вы получили при перетаскивании по сортируемому контейнеру, по-видимому, является ошибкой в ​​пользовательском интерфейсе jquery, но также составляет ровно 100% ширины сортируемого контейнера, поэтому я добавил следующий css:

.sortable .ui-draggable-dragging:not(.ui-sortable-helper){
    margin-left:100%;
}

Вертикальное смещение, которое вы получили после ввода и выхода из сортируемых списков, похоже, вызвано перетаскиваемым полем элементов 4px, которое я удалил. Вы можете достичь того же эффекта с прозрачной рамкой и размером окна: border-box; или добавьте дополнение и установите фоновый клип в поле отступов.

Наконец, анимация в 600 мс, которую вы хотели произойти при удалении элементов, произошла странным образом в моем браузере (Chrome), она перешла в 2 разных местах, прежде чем, наконец, оживилась в нужное место. Вот почему я отключил анимацию. Вы можете попытаться переопределить событие drop в сортируемом jQuery, чтобы вы вручную приближались к правильному местоположению.

Здесь код 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%;
}