Seulement déposer à l’élément qui est vu

J’ai le suivant:

J’essaie de le configurer de sorte que lorsque vous faites glisser l’élément, il ne soit déposé que vers l’élément div que vous pouvez voir et qu’il ne soit pas masqué.

Alors j’ai utilisé ce js:

$(".draggable").draggable({ helper: "clone" }) $("#bottom, .draggable").droppable({ drop: function(event, ui) { var $this = $(this), $dragged = $(ui.draggable); $this.append($dragged.clone()); }, hoverClass: "dragHover" })​ 

Mais il laisse tomber l’élément aux deux endroits, même si une seule des zones de repository n’est pas visible!

Comment puis-je résoudre ce problème afin que cela ne se produise pas ?

Fiddle: http://jsfiddle.net/maniator/Wp4LU/


Extra Info pour recréer la page sans le violon:

HTML:

  
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet

CSS:

 .draggable { border: 1px solid green; background: white; padding: 5px; } .dragHover{ background: blue; } #top { height: 500px; overflow-y: scroll; } #bottom { height: 150px; overflow-y: scroll; border: red solid 4px; } 

Un séjour sans faille

Essayez de régler avec accept fonction. La démo de travail.

 $("#bottom, .draggable").droppable({ drop: function(event, ui) { var $this = $(this), $dragged = $(ui.draggable); $this.append($dragged.clone()); }, accept: function () { var $this = $(this), divTop= $("#top"); if ($this.is(".draggable")) { return $this.offset().top < divTop.offset().top + divTop.height() ; } return true; }, hoverClass: "dragHover" })​;​ 

Si je vous ai bien compris – celui-ci devrait résoudre votre problème – http://jsfiddle.net/Wp4LU/60/ Vous pouvez également écrire une fonction d’acceptation personnalisée – http://jqueryui.com/demos/droppable/#option-accept

Code:

 var draggableList = $('#top'); $(".draggable").draggable({ helper: "clone" }); $("#bottom, .draggable").droppable({ drop: function(event, ui) { var $this = $(this), $dragged = $(ui.draggable); if ($this.hasClass("draggable")) { if ($this.position().top >= draggableList.height() || $this.position().top + $this.outerHeight() >= draggableList.height()) return; } $this.append($dragged.clone()); }, hoverClass: "dragHover" })​;​ 

Selon les sources (jquery.ui.droppable.js), l’opération de suppression recherchera toutes les valeurs réductibles éligibles et appliquera la fonction de suppression à toutes celles qui les intersectent:

 drop: function(draggable, event) { var dropped = false; $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() { if(!this.options) return; if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance)) dropped = this._drop.call(this, event) || dropped; 

(Les anciennes versions avaient la dernière condition “OU” inversée, elle ne s’appliquerait donc qu’à une seule liste de repository. Essayez votre violon à l’aide de jQuery 1.5.2 / jQuery UI 1.8.9 et veillez à ce qu’il ne tombe que dans un élément, ” le mauvais…)

Et chaque mode de tolérance actuellement implémenté dans la fonction $.ui.intersect ne prend en compte que les coordonnées (x, y):

 switch (toleranceMode) { case 'fit': return (l <= x1 && x2 <= r && t <= y1 && y2 <= b); break; case 'intersect': return (l < x1 + (draggable.helperProportions.width / 2) // Right Half && x2 - (draggable.helperProportions.width / 2) < r // Left Half && t < y1 + (draggable.helperProportions.height / 2) // Bottom Half && y2 - (draggable.helperProportions.height / 2) < b ); // Top Half break; ... 

Par conséquent, à moins que quelqu'un ajoute un mode de tolérance prenant en compte l'indice z, votre seule option consiste à contourner le problème d'une manière ou d'une autre. Je suggèrerais d'abord d'append chaque candidat largable à un ensemble et, lorsqu'il est temps de laisser tomber, de ne sélectionner que celui qui est "le plus proche" de l'écran:

 $("#bottom, .draggable").droppable({ over: function(event, ui) { if ( !ui.draggable.data("drop-candidates") ) ui.draggable.data("drop-candidates",[]); ui.draggable.data("drop-candidates").push(this); }, out: function(event, ui) { var that = this, candidates = ui.draggable.data("drop-candidates") || []; ui.draggable.data("drop-candidates", $.grep(candidates, function(e) { return e != that; }); }, drop: function(event, ui) { var $this = $(this), $dragged = $(ui.draggable); var candidates = $.data("drop-candidates").sort(closestToScreen); if ( candidates[0] == this ) $this.append($dragged.clone()); }, hoverClass: "dragHover" })​ 

Maintenant, implémenter le comparateur le plus closestToScreen est la partie la plus délicate. La spécification CSS du W3C explique comment les moteurs de rendu doivent sortinger les éléments à peindre, mais je n’étais pas parvenu à trouver un moyen facile d’accéder à ces informations. J'ai également posé cette question ici à SO, peut-être que quelqu'un trouvera un bon moyen.


PS Si la modification de la source de l'interface utilisateur jQuery est une option, vous pouvez essayer d'implémenter un mode de tolérance prenant en compte l'index z à l'aide de document.getElementFromPoint , comme suggéré par cette réponse à la question suivante:

 var x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width, y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height; switch (toleranceMode) { ... case 'z-index-aware': return document.elementFromPoint(x1,y1) == droppable; break; 

(Cela garantirait que l'élément situé juste en-dessous du coin supérieur gauche du draggable serait considéré comme "assez bon" comme cible de largage - pas idéal, mais meilleur que ce que nous avons jusqu'à présent; une solution similaire pourrait être adaptée coordonnées du pointeur de la souris à la place)

Et, non, vous ne pouvez pas utiliser cette méthode avec la solution de contournement présentée précédemment: au moment où la chute se produit, l’ aide au glissement est l’élément le plus proche de l’écran ... ( Edit: ah! Cela ne fonctionnerait pas si implémenté comme un mode de tolérance soit, pour la même raison ...)

Si vous voulez juste déposer sur un élément, vous pouvez voir que vous pouvez changer de sélecteur:

 $(".draggable:visible").draggable({ helper: "clone" }); $("#bottom, .draggable:visible").droppable({ drop: function(event, ui) { var $this = $(this), $dragged = $(ui.draggable); $this.append($dragged.clone()); }, hoverClass: "dragHover" })​; 

Ou, lorsque vous masquez un élément, changez sa classe déplaçable par quelque chose d’autre.