web-dev-qa-db-fra.com

jQuery draggable montre l'aide au mauvais endroit après le défilement de la page

J'utilise jQuery draggable et droppable pour un système de planification du travail que je développe. Les utilisateurs font glisser les travaux vers un autre jour ou un autre utilisateur, puis les données sont mises à jour à l'aide d'un appel ajax.

Tout fonctionne bien, sauf lorsque je fais défiler la page principale (les tâches apparaissent sur un grand planificateur de semaine qui dépasse le bas de la fenêtre de mon navigateur). Si j'essaie de faire glisser un élément déplaçable ici, l'élément apparaît au-dessus du curseur de ma souris, de la même quantité de pixels que j'ai défilé vers le bas.

J'utilise jQuery 1.6.0 et jQuery UI 1.8.12.

Je suis sûr qu'il y a une fonction de décalage que je dois ajouter mais je ne sais pas où l'appliquer, ni s'il y a une meilleure façon. Voici mon .draggable() code d'initialisation:

$('.job').draggable({
  zIndex: 20,
  revert: 'invalid',
  helper: 'original',
  distance: 30,
  refreshPositions: true,
});

Une idée de ce que je peux faire pour résoudre ce problème?

78
Alex

Il s'agit peut-être d'un rapport de bogue connexe, il existe depuis un certain temps: http://bugs.jqueryui.com/ticket/374

Cela semble se produire sur tous les navigateurs que j'ai testés (Chrome, FF4, IE9). Vous pouvez contourner ce problème de plusieurs manières:

1. Utilisez position:absolute; dans votre css. Les éléments absolument positionnés ne semblent pas être affectés.

2. Assurez-vous que l'élément parent (événement s'il s'agit du corps) a overflow:auto; ensemble. Mon test a montré que cette solution corrige la position, mais elle désactive la fonctionnalité de défilement automatique. Vous pouvez toujours faire défiler à l'aide de la molette de la souris ou des touches fléchées.

3. Appliquez manuellement le correctif suggéré dans le rapport de bogue ci-dessus et testez attentivement s'il provoque d'autres problèmes.

4. Attendez une correction officielle. Il est prévu pour jQuery UI 1.9, bien qu'il ait été reporté plusieurs fois dans le passé.

5. Si vous êtes certain que cela se produit sur tous les navigateurs, vous pouvez placer ces hacks dans les événements des draggables affectés pour corriger les calculs. Il y a beaucoup de navigateurs différents à tester, donc il ne doit être utilisé qu'en dernier recours:

$('.drag').draggable({
   scroll:true,
   start: function(){
      $(this).data("startingScrollTop",$(this).parent().scrollTop());
   },
   drag: function(event,ui){
      var st = parseInt($(this).data("startingScrollTop"));
      ui.position.top -= $(this).parent().scrollTop() - st;
   }
});
104
DarthJDG

Cette solution fonctionne sans ajuster le positionnement de quoi que ce soit, il vous suffit de cloner l'élément et de le rendre absolument positionné.

$(".sidebar_container").sortable({
  ..
  helper: function(event, ui){
    var $clone =  $(ui).clone();
    $clone .css('position','absolute');
    return $clone.get(0);
  },
  ...
});

L'assistant peut être une fonction qui doit renvoyer l'élément DOM avec lequel glisser.

43
mordy

Cela a fonctionné pour moi:

start: function (event, ui) {
   $(this).data("startingScrollTop",window.pageYOffset);
},
drag: function(event,ui){
   var st = parseInt($(this).data("startingScrollTop"));
   ui.position.top -= st;
},
11
Ashraf Fayad

Désolé d'avoir écrit une autre réponse. Comme aucune des solutions dans la réponse ci-dessus ne pouvait être utilisée par moi, j'ai fait beaucoup de recherches sur Google et j'ai fait de nombreuses modifications mineures frustrantes avant de trouver une autre solution raisonnable.

Ce problème semble se produire chaque fois que n'importe lequel des éléments parents position est défini sur "relatif". J'ai dû remanier mon balisage et modifier mon CSS, mais en supprimant cette propriété de tous les parents, j'ai réussi à faire fonctionner correctement .sortable() dans tous les navigateurs.

7
Joshua Bambrick

Il semble que ce bogue se répète très souvent, et chaque fois qu'il y a une solution différente. Rien de ce qui précède, ni rien d'autre que j'ai trouvé sur Internet n'a fonctionné. J'utilise jQuery 1.9.1 et Jquery UI 1.10.3. Voici comment je l'ai corrigé:

$(".dragme").draggable({
  appendTo: "body",
  helper: "clone",
  scroll: false,
  cursorAt: {left: 5, top: 5},
  start: function(event, ui) {
    if(! $.browser.chrome) ui.position.top -= $(window).scrollTop();
  },
  drag: function(event, ui) {
    if(! $.browser.chrome) ui.position.top -= $(window).scrollTop();
  }
});

Fonctionne en FF, IE, Chrome, je ne l'ai pas encore testé dans d'autres navigateurs.

7
kazmer

Je supprime le débordement:

html {overflow-y: scroll; background: #fff;}

Et ça marche parfaitement!

5
Miguel Puig

Ce bogue a été déplacé vers http://bugs.jqueryui.com/ticket/6817 et, il y a environ 5 jours (16 décembre 2013 ou à peu près) semble avoir finalement été corrigé. La suggestion est maintenant d'utiliser la dernière version de développement de http://code.jquery.com/ui/jquery-ui-git.js ou d'attendre version 1.10.4 qui devrait contenir ce correctif.

Edit: Il semble que ce correctif fasse maintenant partie de http://bugs.jqueryui.com/ticket/9315 qui ne devrait pas tomber avant la version 1.11. L'utilisation de la version de contrôle de source liée ci-dessus de jQuery semble résoudre le problème pour moi et @Scott Alexander (commentaire ci-dessous).

5
Patrick

Semble remarquable que ce bogue n'ait pas été résolu depuis si longtemps. Pour moi, c'est un problème sur Safari et Chrome (c'est-à-dire les navigateurs webkit) mais pas sur IE7/8/9 ni sur Firefox qui fonctionnent tous très bien.

J'ai trouvé que le réglage absolu ou fixe, avec ou sans! Important, n'a pas aidé, donc à la fin j'ai ajouté une ligne à ma fonction de gestionnaire de glisser:

ui.position.top += $( 'body' ).scrollTop();

Je m'attendais à devoir rendre cette ligne spécifique au webkit mais curieusement, cela fonctionnait bien partout. (Attendez-vous à un commentaire de ma part disant "euh non, en fait, il a foiré tous les autres navigateurs".)

4
Allen

ce que j'ai fait c'est:

$("#btnPageBreak").draggable(
        {
          appendTo: 'body',
          helper: function(event) {
            return '<div id="pageBreakHelper"><img  id="page-break-img"  src="page_break_cursor_red.png" /></div>';
          },
          start: function(event, ui) {
          },
          stop: function(event, ui) {
          },
          drag: function(event,ui){
            ui.helper.offset(ui.position);
         }
        });
3
Pankaj Sharma

J'ai eu le même problème et j'ai découvert que tout cela était dû à une classe bootstrap navbar "navbar-fixed-top" avec la position à fixed, qui descend de mon <body> 60 pixels de moins que <html> Haut. Ainsi, lorsque je déplaçais des formulaires glissables sur mon site, le curseur apparaissait à 60 pixels en haut du formulaire.

Vous pouvez voir que dans Chrome outil de débogage, dans l'interface Elements, cliquez sur le <body> tag et vous verrez un espace entre le haut et le corps.

Étant donné que j'utilise cette barre de navigation dans toutes mes applications et que je ne voulais pas modifier mon appel d'initialisation pour le faire glisser (selon la procédure de DarthJDG) pour chaque formulaire. J'ai décidé d'étendre le widget déplaçable de cette façon et d'ajouter simplement un yOffset dans mon initialisation déplaçable.

(function($) {
  $.widget("my-ui.draggable", $.ui.draggable, {
    _mouseDrag: function(event, noPropagation) {

      //Compute the helpers position
      this.position = this._generatePosition(event);
      this.positionAbs = this._convertPositionTo("absolute");

      //Call plugins and callbacks and use the resulting position if something is returned
      if (!noPropagation) {
        var ui = this._uiHash();
        if(this._trigger('drag', event, ui) === false) {
          this._mouseUp({});
          return false;
        }
        this.position = ui.position;
      }
      // Modification here we add yoffset in options and calculation
      var yOffset = ("yOffset" in this.options) ? this.options.yOffset : 0;

      if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
      if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top-yOffset+'px';
      if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);

      return false;
    }
  });
})(jQuery);

Maintenant, lorsque j'initialise l'élément/le formulaire déplaçable dans un site en utilisant bootstrap navbar:

// Make the edit forms draggable
$("#org_account_pop").draggable({handle:" .drag_handle", yOffset: 60});

Bien sûr, vous devrez expérimenter pour déterminer le bon décalage selon votre propre site.

Merci quand même à DarthJDG qui m'a indiqué la bonne direction. À votre santé!

1
fled

Je ne suis pas entièrement sûr que cela fonctionnera dans tous les cas, mais cette solution de contournement que je viens de travailler pour moi sur toutes les différentes situations que j'ai dû tester (et où les solutions proposées précédemment données ici et ailleurs ont toutes échoué)

(function($){
$.ui.draggable.prototype._getRelativeOffset = function()
{
    if(this.cssPosition == "relative") {
        var p = this.element.position();
        return {
            top: p.top - (parseInt(this.helper.css("top"),10) || 0)/* + this.scrollParent.scrollTop()*/,
            left: p.left - (parseInt(this.helper.css("left"),10) || 0)/* + this.scrollParent.scrollLeft()*/
        };
    } else {
        return { top: 0, left: 0 };
    }
};
}(jQuery));

(Je l'ai soumis au tracker jQuery.ui pour voir ce qu'ils en pensent .. serait cool si ce bug de 4 ans pouvait être finalement corrigé: /)

1
vor

J'ai réussi à résoudre le même problème en ajoutant display: inline-block aux éléments glissés

Ajouter position: relative n'a PAS fonctionné pour moi (jQuery le remplace par position: absolute au démarrage par glisser)

Versions jQuery utilisées:

  • jQuery - 1.7.2
  • jQuery UI - 1.11.4
1
Plamen

Cela semble faire le contournement sans avoir besoin d'utiliser la position: absolue dans le parent :)

$("body").draggable({
     drag: function(event, ui) { 
         return false; 
     } 
});
1
TomCe

C'est ce qui a fonctionné pour moi, après avoir adapté tout ce que j'ai lu sur le problème.

$(this).draggable({
  ...
  start: function (event, ui) {
    $(this).data("scrollposTop", $('#elementThatScrolls').scrollTop();
    $(this).data("scrollposLeft", $('#elementThatScrolls').scrollLeft();
  },
  drag: function (event, ui) {
    ui.position.top += $('#elementThatScrolls').scrollTop();
    ui.position.left += $('#elementThatScrolls').scrollLeft();
  },
  ...
}); 

* elementThatScrolls est le parent ou l'ancêtre avec débordement: auto;

Détermine la position de l'élément de défilement et ajoute à la position de l'assistant à chaque fois qu'il est déplacé/glissé.

J'espère que cela aide quelqu'un, car j'ai perdu beaucoup de temps à ce sujet.

1
Rnubi

J'utilise JQuery draggable sur Firefox 21.0 et j'ai le même problème. Le curseur reste un pouce au-dessus de l'image d'assistance. Je pense que c'est un problème dans le Jquery lui-même qui n'a toujours pas été résolu. J'ai trouvé une solution de contournement à ce problème, qui utilise la propriété "cursorAt" du draggable. Je l'ai utilisé comme suit, mais on peut changer cela en fonction de ses besoins.

$('.dragme').draggable({
helper: 'clone',    
cursor: 'move',    
cursorAt: {left: 50, top: 80}
});

Remarque: Cela sera applicable à tous les navigateurs, donc après avoir utilisé ce code, vérifiez votre page dans tous les navigateurs pour obtenir les positions gauche et supérieure correctes.

1
impiyush

J'utilise un nav collant en haut et pensais que c'était ce qui causait le problème de défilement que tout le monde semble avoir. J'ai essayé l'idée de tout le monde avec cursorAt, j'ai essayé le confinement, et rien n'a fonctionné SAUF le fait de régler mon débordement html dans le CSS! C'est fou comment un petit CSS va tout gâcher. C'est ce que j'ai fait et ça marche comme un charme:

html {
  overflow-x: unset;
}

body {
  overflow-x: hidden;
}
0
Altus Nix

J'ai essayé différentes réponses ici, y compris la mise à jour de jQuery et j'ai constaté que cela fonctionnait pour moi. J'ai testé cela sur la dernière chrome et IE. Je suppose que ce sera un problème avec différentes solutions en fonction de la disposition de votre interface utilisateur.

$('{selector}').draggable({
    start: function(event, ui) {
        ui.position.top -= $(document).scrollTop();
    },
    drag: function(event, ui) {
        ui.position.top -= $(document).scrollTop();
    }
});
0
Lee Willis

J'ai arrêté d'utiliser dragQuable jQueryUi et déplacé d'utiliser un meilleur plugin appelé jquery.even.drag , une taille de code beaucoup plus petite, sans toute la merde que jQueryUI a.

J'ai fait une page de démonstration en utilisant ce plugin dans un conteneur qui a la position: fixe

Démo

J'espère que cela aide quelqu'un, car ce problème est GRAND.

0
vsync

Utilisation appendTo: "body' et définissez la position avec cursorAt. Cela fonctionne très bien avec moi.

Example of my icon following the mouse cursor even after scrolling the page

$('.js-tire').draggable
      tolerance: 'fit',
      appendTo: 'body',
      cursor: 'move',
      cursorAt:
        top: 15,
        left: 18
      helper: (event) ->
        $('<div class="tire-icon"></div>').append($('<img src="/images/tyre_32_32.png" />'))
      start: (event, ui) ->
        if $(event.target).hasClass 'in-use'
          $('.js-send-to-maintenance-box').addClass 'is-highlighted'
        else
          $('.ui-droppable').addClass 'is-highlighted'
      stop: (event, ui) ->
        $('.ui-droppable')
          .removeClass 'is-highlighted'
          .removeClass 'is-dragover'
0
Michelle Diniz

Pourquoi ne pas prendre la position du curseur? j'utilise le plugin triable et je le corrige avec ce code:

sort: function(e, ui) {
    $(".ui-sortable-handle.ui-sortable-helper").css({'top':e.pageY});
}
0
Aztrozero

J'ai eu un problème similaire, uniquement avec certains IE 10 fonctionnant sous Windows 8. Tous les autres navigateurs ont bien fonctionné. La suppression de cursorAt: {top: 0} a résolu le problème.

0
nmm