web-dev-qa-db-fra.com

Rétablir un objet déplaçable jQuery dans son conteneur d'origine lors d'un événement out de droppable

J'ai un objet déplaçable qui, s'il n'est pas déposé dans une liste de dépôt, va revenir. Cela fonctionne bien jusqu'à ce qu'un utilisateur supprime un élément dans la liste de dépôt. S'ils décident qu'ils ont commis une erreur à tout moment, ils retirent le déplaçable, il revient au largage. Je préférerais que sur et désactiver le draggable retourne à son conteneur d'origine.

Mon code est ci-dessous mais j'ai fourni un exemple sur jsFiddle .

HTML

<div id="Origin">
    <div id="draggable" class="ui-widget-content">
        <p>I revert when I'm not dropped</p>
    </div>
</div>
<div id="droppable" class="ui-widget-header">
    <p>Drop me here</p>
</div>

JavaScript

$(function() {
    $("#draggable").draggable({ 
        revert:  function(dropped) {
           var dropped = dropped && dropped[0].id == "droppable";
           if(!dropped) alert("I'm reverting!");
           return !dropped;
        } 
    }).each(function() {
        var top = $(this).position().top;
        var left = $(this).position().left;
        $(this).data('orgTop', top);
        $(this).data('orgLeft', left);
    });

    $("#droppable").droppable({
        activeClass: 'ui-state-hover',
        hoverClass: 'ui-state-active',
        drop: function(event, ui) {
            $(this).addClass('ui-state-highlight').find('p').html('Dropped!');
        },
        out: function(event, ui) {
                // doesn't work but something like this
                ui.draggable.mouseup(function () {
                var top = ui.draggable.data('orgTop');
                var left = ui.draggable.data('orgLeft');
                ui.position = { top: top, left: left };
            });
        }
    });
});
59
ahsteele
$(function() {
    $("#draggable").draggable({
        revert : function(event, ui) {
            // on older version of jQuery use "draggable"
            // $(this).data("draggable")
            // on 2.x versions of jQuery use "ui-draggable"
            // $(this).data("ui-draggable")
            $(this).data("uiDraggable").originalPosition = {
                top : 0,
                left : 0
            };
            // return boolean
            return !event;
            // that evaluate like this:
            // return event !== false ? false : true;
        }
    });
    $("#droppable").droppable();
});
125
Luca Filosofi

Je ne sais pas si cela fonctionnera pour votre utilisation réelle, mais cela fonctionne dans votre cas de test - mis à jour à http://jsfiddle.net/sTD8y/27/ .

Je viens de faire en sorte que le retour intégré ne soit utilisé que si l'élément n'a pas été supprimé auparavant. S'il a été abandonné, le retour est effectué manuellement. Vous pouvez ajuster ceci pour animer un décalage calculé en vérifiant les propriétés CSS réelles, mais je vous laisserai jouer avec cela, car cela dépend en grande partie du CSS du draggable et de sa structure DOM environnante.

$(function() {
    $("#draggable").draggable({
        revert:  function(dropped) {
             var $draggable = $(this),
                 hasBeenDroppedBefore = $draggable.data('hasBeenDropped'),
                 wasJustDropped = dropped && dropped[0].id == "droppable";
             if(wasJustDropped) {
                 // don't revert, it's in the droppable
                 return false;
             } else {
                 if (hasBeenDroppedBefore) {
                     // don't rely on the built in revert, do it yourself
                     $draggable.animate({ top: 0, left: 0 }, 'slow');
                     return false;
                 } else {
                     // just let the built in revert work, although really, you could animate to 0,0 here as well
                     return true;
                 }
             }
        }
    });

    $("#droppable").droppable({
        activeClass: 'ui-state-hover',
        hoverClass: 'ui-state-active',
        drop: function(event, ui) {
            $(this).addClass('ui-state-highlight').find('p').html('Dropped!');
            $(ui.draggable).data('hasBeenDropped', true);
        }
    });
});
7
BBonifield

Au cas où quelqu'un serait intéressé, voici ma solution au problème. Il fonctionne de manière totalement indépendante des objets pouvant être déplacés, en utilisant plutôt des événements sur l'objet Droppable Ça marche plutôt bien:

$(function() {
    $(".draggable").draggable({
        opacity: .4,
        create: function(){$(this).data('position',$(this).position())},
        cursor:'move',
        start:function(){$(this).stop(true,true)}
    });

    $('.active').droppable({
        over: function(event, ui) {
            $(ui.helper).unbind("mouseup");
        },
        drop:function(event, ui){
            snapToMiddle(ui.draggable,$(this));
        },
        out:function(event, ui){
            $(ui.helper).mouseup(function() {
                snapToStart(ui.draggable,$(this)); 
            });
        }
    });
}); 

function snapToMiddle(dragger, target){
    var topMove = target.position().top - dragger.data('position').top + (target.outerHeight(true) - dragger.outerHeight(true)) / 2;
    var leftMove= target.position().left - dragger.data('position').left + (target.outerWidth(true) - dragger.outerWidth(true)) / 2;
    dragger.animate({top:topMove,left:leftMove},{duration:600,easing:'easeOutBack'});
}
function snapToStart(dragger, target){
    dragger.animate({top:0,left:0},{duration:600,easing:'easeOutBack'});
}
3
bnrup

Si vous souhaitez rétablir l'élément à la position source s'il n'est pas déposé à l'intérieur d'un élément #droppable, enregistrez simplement l'élément parent d'origine de l'élément glissable au début du script (au lieu de la position), et si vous vérifiez qu'il n'est pas supprimé dans #droppable, puis restaurez simplement le parent de #draggable à cet élément d'origine.

Alors, remplacez ceci:

}).each(function() {
    var top = $(this).position().top;
    var left = $(this).position().left;
    $(this).data('orgTop', top);
    $(this).data('orgLeft', left);
});

avec ça:

}).each(function() {
    $(this).data('originalParent', $(this).parent())
});

Ici, vous aurez l'élément parent d'origine du glissable. Maintenant, vous devez restaurer son parent dans un moment précis.

drop est appelé à chaque fois que l'élément est sorti de la liste de dépôt, pas à l'arrêt. Donc, vous ajoutez beaucoup de rappels d'événements. C'est faux, car vous ne nettoyez jamais l'événement mouseup. Un bon endroit où vous pouvez créer un rappel et vérifier si l'élément a été déposé à l'intérieur ou à l'extérieur de l'élément #droppable est revert et que vous le faites maintenant, supprimez simplement le rappel drop.

Lorsque l'élément est supprimé et qu'il doit savoir s'il doit être annulé ou non, vous savez à coup sûr que l'utilisateur n'aura plus aucune interaction avant le début du nouveau déplacement. Donc, en utilisant la même condition que vous utilisez pour savoir s’il faut revenir en arrière ou le savoir, remplaçons cette alert par un fragment de code qui: restaure l’élément parent à la div d'origine et réinitialise la originalPosition à partir de la interne draggable. Le __propriété originalPosition est défini au moment de _mouseStart. Ainsi, si vous changez le propriétaire de l'élément, vous devez le réinitialiser, afin que l'animation de la restauration revienne au bon endroit. Définissons donc ceci sur {top: 0, left: 0}, faisant passer l'animation au point d'origine de l'élément:

revert: function(dropped) {
    var dropped = dropped && dropped[0].id == "droppable";
    if(!dropped) {
        $(this).data("draggable").originalPosition = {top:0, left:0}
        $(this).appendTo($(this).data('originalParent'))
    }
    return !dropped;
}

Et c'est tout! Vous pouvez vérifier cela en travaillant ici: http://jsfiddle.net/eUs3e/1/

Notez que, si dans la mise à jour de l'interface utilisateur de jQuery, le comportement de revert ou originalPosition change, vous devrez mettre à jour votre code pour que cela fonctionne. Garde en tête que.

Si vous avez besoin d'une solution qui n'utilise pas d'appels aux composants internes de ui.draggable, vous pouvez transformer votre body en un élément réductible avec l'option greedy définie par false. Vous devrez vous assurer que vos éléments body prennent tout l'écran.

Bonne chance!

3
Gonzalo Larralde

Il est lié à revenir à l'origine: pour définir l'origine lorsque l'objet est glissé: utilisez simplement $(this).data("draggable").originalPosition = {top:0, left:0};

Par exemple: j'utilise comme ça

               drag: function() {
                    var t = $(this);
                    left = parseInt(t.css("left")) * -1;
                    if(left > 0 ){
                        left = 0;
                        t.draggable( "option", "revert", true );
                        $(this).data("draggable").originalPosition = {top:0, left:0};
                    } 
                    else t.draggable( "option", "revert", false );

                    $(".slider-work").css("left",  left);
                }
1
Hugo Gresse

J'ai trouvé un autre moyen simple de gérer ce problème, il vous suffit de l'attribut "connectToSortable:" pour le déplacer comme ci-dessous: 

$("#a1,#a2").draggable({
        connectToSortable: "#b,#a",
        revert: 'invalid',
    });

PS: Plus de détails et exemple
Comment déplacer des objets déplaçables entre la zone source et la zone cible avec jQuery

0
Willie Cheng