Comment réparer les helpers draggables mal positionnés pour les objets durables connectés (en partie causé par des éléments parents flottants/positionnés relatifs)?

Préface

j'ai un problème lorsque l'Assistant est décalé de façon incorrecte, en utilisant des draggables + sortables qui sont placés dans des éléments parents flottants et positionnés de façon relative. Les éléments parents flottés sont des colonnes Bootstrap, où plusieurs listes sortables sont placées dans une colonne, et une liste de draggables est placée dans une autre.

Exemple

voici un exemple pratique extrait de

$('.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>

Mettre À Jour Le 16 Nov 2015 j'ai modifié l'exemple de code pour mieux refléter mon contexte d'utilisation réel, où il y a d'autres lignes au-dessus de celle qui contient les draggables/sortables.

démo, et une image montrant ce qui se passe

enter image description here

autres explications

lorsqu'on en traîne un des draggables de la colonne de droite au-dessus d'une des listes sortables sur le côté gauche, et ne pas le laisser tomber, mais le traîner plus loin de la boîte de limite des listes sortables, l'Assistant est positionné incorrectement, il déplace quelques centaines de pixels vers la gauche, comme si elle incorpore incorrectement une sorte de décalage (on dirait qu'il pourrait être la position originale de draggable).

fait intéressant, cela ne se produit pas lorsque les draggables sont placés dans le même parent élément comme les sortables, au moins il ne se déplace pas horizontalement, mais verticalement lors du déplacement du draggable rapide Haut/Bas ou gauche/droite dans/hors de la liste sortable.

le déplacement horizontal est en quelque sorte lié à des éléments parents flottants/positionnés de façon relative, désactivant le flottant ou la position relative le fixe. Cependant, j'aimerais garder ceci tel quel, et trouver une autre solution. Le déplacement vertical se produit également quand le positionnement flottant/relatif n'est pas impliqué, donc je pense qu'il y a un peu plus de ce problème.

Mettre À Jour Le 15 Novembre 2015 - jQuery UI changes

ressemble à jQuery UI 1.11.4 changé le comportement un peu, maintenant il ne sera pas immédiatement basculer horizontalement le moment où vous quittez la boîte de limite d'un sortable, mais vous devez vous déplacer entre deux ou plusieurs sortables d'abord. A part ça, le comportement du buggy semble inchangé.

Mettre À Jour Le 16 Nov 2015 - appendTo option

initialement j'ai essayé d'utiliser appendTo une solution de contournement, car de cette façon l'Assistant serait maintenu en dehors des listes, et bien que cela soit vrai pour mon code d'exemple original, cela ne fonctionnera pas avec le code d'exemple mis à jour, où d'autres lignes sont placées au-dessus de celles qui contiennent les draggables/sortables, elles provoquent le déplacement vertical de l'Assistant.

Question

est-ce que quelqu'un sait d'où vient le décalage horizontal problématique? exactement, et comment le corriger tout en continuant à utiliser des éléments parents flottants / positionnés relatifs?

et qu'en est-il de la compensation verticale, vu que cela se produit dans l'INTERFACE utilisateur de jQuery demo trop, me fait penser que c'est un bug qui n'est pas lié au style de l'élément parent?

Mettre À Jour Le 15 Novembre 2015 - problème de décalage Vertical localisé

décalage vertical semble avoir à faire avec la marge appliquée sur les draggables, sans cela il semble fonctionner très bien.

j'ai signalé les deux bugs, mais je suis toujours à la recherche d'un correctif/contournement que je peux appliquer moi-même jusqu'à ce que cela puisse ou ne puisse pas être corrigé dans la bibliothèque.

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

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

19
demandé sur TylerH 2015-11-13 03:22:55

3 réponses

Ce qui est étrange, c'est qu'il semble mieux fonctionner avec jquery-ui 10.4. La différence est que dans 10,4 déplaçable helper reste dans sa div originale, et est cloné dans les sortables mais caché. Les calculs sont donc plus faciles à faire.

en 11.4, le helper est ajouté à la variable sortable sur laquelle il est traîné, ce qui rend les calculs de déport précis difficiles à suivre. Vous devez constamment changer l'offset parent, et garder piste sur laquelle elle est sortable, sur laquelle elle était sortable, la position du sortable et ainsi de suite. Et il y a clairement un bug là-bas.

Une solution simple serait d'obtenir l' connectToSortable plugin à partir de 10.4. Vous aurez à vérifier les effets secondaires indésirables, mais rapidement, il semble être au travail. Vous pouvez utiliser un nom différent pour garder l'original. Comme ceci:

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

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

EDIT:

je ne pense pas que le supplément de div est ce qui cause le problème, c'est vraiment un bug avec le mode connectToSortable fonctionne en jquery 11.4 qui est à l'origine du problème. Pour permettre le déplacement de l'aide dans le sortables et toujours garder la trace de l'offset approprié, vous devez réajuster quelques données chaque fois que l'Assistant change div. Il y a deux défauts dans la façon de faire:

Première l'une est qu'il y a un refreshOffsets méthode qui est commun à autres événements en déplaçable. Il est utilisé par exemple lorsque vous cliquez sur un déplaçable. Et donc il essaie de calculer l'offset basé sur cliquer. Mais lors de l'appel de refreshOffsets de l'événement sortable, il mess cliquez sur le décalage. Cela peut être résolu en changeant refreshOffsets méthode pour ne pas considérer le événement.pageX et Y. Comme ceci:

$.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;
    }

L'autre problème se produit parce que vous avez beaucoup d' sortables. Fondamentalement l'autre opération qui doit être fait est de changer le parent offset. La façon dont il est fait en ce moment est qu'il sauve le précédent parent. Normalement ça fonctionne, mais si vous vous déplacez trop vite, la séquence fait en sorte que le sauvé des parents est un sortable et pas l'original parent. Vous pouvez corriger cela en sauvant le parent sur le début de traînée, qui dans tout cas fait semble plus logique. Comme ceci:

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

Voir ici: http://jsfiddle.net/24a8q49j/1/

6
répondu Julien Grégoire 2015-11-20 03:39:33

une solution de contournement JavaScript simple est de stocker l'offset de la souris lorsque le déplacement commence et de forcer le ui.helper pour avoir la même position de la souris - toujours:

[...]
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();

voir le mise à jour de jsfiddle.

pour le cas simple il s'agit d'une solution de contournement facile, mais nécessite plus de travail si vous introduisez des frontières (où l'assistant ne peut pas quitter) etc.


Edit: Pour votre extrait de je suis venu avec une autre solution (parce que l'autre ne fonctionne pas comme prévu - plus difficile d'obtenir les compensations initiales que je pensais ...): Il suffit de forcer l'élément à rester au pointeur de la souris, quel que soit le décalage initial, après avoir ajouté l'assistant au corps. Ce n'est pas 100% gentil, parce que quand on commence à faire glisser il pourrait "sauter" à la souris - mais alors il reste là au moins ...

$('.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
répondu Markus 2015-11-22 11:36:07

voir https://jsfiddle.net/tsLcjw9c/1/

j'ai mis une largeur fixe sur la liste des draggables (100px) de sorte que l'Assistant draggable ait la même largeur que les éléments de la liste, ce qui empêche le décalage horizontal initial.

le gros décalage horizontal que vous avez obtenu en glissant sur le conteneur sortable semble en effet être un bug dans jQuery UI mais il semble aussi être exactement 100% de la largeur du conteneur sortable, ce qui est pourquoi j'ai ajouté le suivant css:

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

le décalage vertical que vous avez obtenu après avoir entré et quitté les listes sortables semblait être causé par la marge de 4px des articles sortables, que j'ai supprimé. Vous pouvez obtenir le même effet avec une bordure transparente et un box-sizing:border-box; ou bien ajouter un rembourrage et définir background-clip à padding-box.

enfin l'animation de 600ms que vous vouliez réaliser en laissant tomber les items s'est passée d'une manière étrange dans mon navigateur (Chrome), elle est passée à 2 différents endroits avant de finalement animer vers le bon endroit. C'est pourquoi j'ai maintenant désactivé l'animation. Vous pouvez essayer de surcharger l'événement drop dans le jquery sortable, de sorte que vous animez manuellement vers le bon endroit.

voici le code 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%;
}
1
répondu Hacktisch 2015-11-16 13:35:31