Верните объект перетаскивания jQuery обратно в исходный контейнер на событие droppable

У меня есть перетаскиваемый элемент, который, если не упадет в droppable, вернется. Это хорошо работает, пока пользователь не опустит элемент в раскрывающемся списке. Если они решат, что совершили ошибку в любое время, когда они вытащили перетащив, она вернется к droppable. Я бы предпочел, чтобы выключение и деактивация draggable возвращается в исходный контейнер.

Мой код ниже, но я предоставил образец в 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 };
            });
        }
    });
});

Ответ 1

$(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();
});

Ответ 2

Я не уверен, что это будет работать для вашего фактического использования, но оно работает в вашем тестовом примере - обновлено на http://jsfiddle.net/sTD8y/27/.

Я просто сделал это так, чтобы встроенный возврат использовался, только если элемент не был отброшен раньше. Если он был сброшен, возврат выполняется вручную. Вы можете настроить это, чтобы оживить какое-то вычисленное смещение, проверив фактические свойства CSS, но я позволю вам играть с этим, потому что многое зависит от CSS перетаскиваемого объекта и его структуры DOM.

$(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 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);
        }
    });
});

Ответ 3

Если вы хотите вернуть элемент в исходную позицию, если он не выпал внутри элемента #droppable, просто сохраните исходный родительский элемент перетаскиваемого в начале script (вместо позиции) и если вы убедитесь, что он не упал в #droppable, а затем просто восстановите родительский элемент #draggable для этого исходного элемента.

Итак, замените это:

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

с этим:

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

Здесь вы будете иметь исходный родительский элемент перетаскиваемого. Теперь вам нужно восстановить его родитель в определенный момент.

drop вызывается каждый раз, когда элемент вытягивается из droppable, а не на остановке. Таким образом, вы добавляете много обратных вызовов событий. Это неправильно, потому что вы никогда не очищаете событие mouseup. Хорошее место, где вы можете перехватить обратный вызов и проверить, был ли элемент удалено внутри или вне элемента #droppable, составляет revert, и вы делаете это прямо сейчас, поэтому просто удалите обратный вызов drop.

Когда элемент отбрасывается и ему нужно знать, нужно ли его возвращать или нет, вы точно знаете, что у вас не будет никакого другого взаимодействия с пользователем до начала нового перетаскивания. Таким образом, используя то же условие, которое вы используете, чтобы знать, следует ли его возвращать или знать, замените этот alert фрагментом кода, который: восстанавливает родительский элемент в исходный div и сбрасывает originalPosition из draggable внутренности. Проэкт originalPosition устанавливается во время _mouseStart, поэтому, если вы меняете владельца элемента, вы должны reset его, чтобы анимация возврата вернулась в нужное место. Таким образом, установите значение {top: 0, left: 0}, чтобы анимация переместилась в исходную точку элемента:

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

И это! Вы можете проверить это здесь: http://jsfiddle.net/eUs3e/1/

Учтите, что если в любом обновлении пользовательского интерфейса jQuery поведение revert или originalPosition изменяется, вам необходимо обновить код, чтобы он работал. Имейте в виду, что.

Если вам нужно решение, которое не использует вызовы внутренних элементов ui.draggable, вы можете сделать свой body элемент с возможностью удаления с greedy опцией, определенной как false. Вам нужно будет убедиться, что ваши элементы body будут отображаться в полноэкранном режиме.

Удачи!

Ответ 4

В случае, если кому-то интересно, вот мое решение проблемы. Он полностью работает независимо от объектов Draggable, вместо этого использует события в объекте Droppable. Он работает достаточно хорошо:

$(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'});
}

Ответ 5

Это связано с возвратным началом: установить начало координат, когда объект перетаскивается: просто используйте $(this).data("draggable").originalPosition = {top:0, left:0};

Например: я использую как это

               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);
                }