Возвратить объект jQuery перетаскиваемым обратно в оригинальный контейнер на случай десантирования


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

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

6 ответов:

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

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

Я просто сделал это так, что встроенный revert используется только в том случае, если элемент не был удален раньше. Если он был удален, возврат выполняется вручную. Вы можете настроить это для анимации до некоторого расчетного смещения, проверив фактические свойства 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'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);
        }
    });
});

если вы хотите вернуть элемент в исходное положение, если он не упал внутри #droppable элемент, просто сохраните исходный родительский элемент перетаскиваемого в начале скрипта (вместо позиции), и если вы убедитесь, что он не упал в #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 proeprty устанавливается в то время из _mouseStart, так что, если вы измените владельца элемента, вы должны сбросить его, чтобы сделать анимацию revert перейти в нужное место. Итак, давайте установим это {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 изменения, вам потребуется чтобы обновить код, чтобы заставить его работать. Имейте это в виду.

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

удачи!

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

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

это связано с revert origin : чтобы установить origin, когда объект перетаскивается : просто используйте $(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);
                }

Я нашел еще один простой способ справиться с этой проблемой, вам просто нужен атрибут "connectToSortable:" для перетаскивания, как показано ниже кода:

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

PS: подробнее и пример
как перемещать Перетаскиваемые объекты между исходной областью и целевой областью с помощью jQuery