j-ulrich / jquery-simulate-ext

jQuery simulate extended
https://j-ulrich.github.io/jquery-simulate-ext/
Other
146 stars 48 forks source link

Simulate() drag-n-drop doesn't seem to work twice on the same element #26

Closed evilkillerfiggin closed 9 years ago

evilkillerfiggin commented 9 years ago

I have divs that are setup as draggable() by the following code:

    $(div).addClass('component-stage').draggable({
        revert:true,
        helper:'clone'
    })

On the page, the user can drag them from a shelf called #available-stages onto other shelves. In testing, I'm doing that like this:

    $('#available-stages').children('.component-stage[component_id="1"]')
    .simulate("drag-n-drop", {
        dropTarget: $('#new-proc-parts-out')
    });
    $('#available-stages').children('.component-stage[component_id="2"]')
    .simulate("drag-n-drop", {
        dropTarget: $('#new-proc-parts-out')
    });

This works fine when I do it by hand, and fine when done by .simulate(), until I try to drag the same div to two different panels:

    $('#available-stages').children('.component-stage[component_id="2"]')
    .simulate("drag-n-drop", {
        dropTarget: $('#new-proc-parts-out')
    });
    $('#available-stages').children('.component-stage[component_id="2"]')
    .simulate("drag-n-drop", {
        dropTarget: $('#new-proc-parts-in')
    });

Doing this works fine by hand, but when the above code tries to do it it seems to silently ignore the second command. There are no errors, #new-proc-parts-out contains the correct div, but #new-proc-parts-in is empty.

NB: I hope this is the right venue for this kind of problem. I didn't see anywhere else after a quick Google search.

j-ulrich commented 9 years ago

Yes, you are correct here. :-)

I guess that the problem is, that the events are handled asynchronously. In other words: both simulation are performed but simultaneously and therefore it's "random" which one finishes first and is therefore not visible.

Could you try it like this and see if it fixes the problem:

$('#available-stages').children('.component-stage[component_id="2"]')
    .simulate("drag-n-drop", {
        dropTarget: $('#new-proc-parts-out'),
        callback: function() {
            $('#available-stages').children('.component-stage[component_id="2"]')
                .simulate("drag-n-drop", {
                dropTarget: $('#new-proc-parts-in')
            });
        }
});
evilkillerfiggin commented 9 years ago

I'm afraid I've tried that already and I get the same result. I've also tried with a busy wait between the two simulate() calls.

evilkillerfiggin commented 9 years ago

Okay, just tried again using a callback and a setTimeout() like this:

    $('#available-stages').children('.component-stage[component_id="2"]')
    .simulate("drag-n-drop", {
        dropTarget: $('#new-proc-parts-out'),
        callback: function(){
            setTimeout(function(){
                $('#available-stages').children('.component-stage[component_id="2"]')
                .simulate("drag-n-drop", {
                    dropTarget: $('#new-proc-parts-in')
                });
            }, 1000)
        }
    });

This now works. Of course, all my tests now fail because they aren't in the setTimeout() function...

evilkillerfiggin commented 9 years ago

Okay, it's messed up my code a lot - have to stick everything in successive setTimeout() functions with 1ms duration in order to get everything running in the right order.

But the simulate() does do its bit, so I'll close this issue.

I don't suppose there's any way to 'force instantaneous' or anything, that would let me run it as I did originally and save the rest of my test code from unnecessary tangles?

j-ulrich commented 9 years ago

I don't suppose there's any way to 'force instantaneous' or anything, that would let me run it as I did originally and save the rest of my test code from unnecessary tangles?

I am wondering why the setTimeout() call is required within the callback function. Maybe that's a bug.

However, I'm afraid there is no other way to synchronize the two drags. And if you don't know it already, you should have a look at jQuery's Deferred object to chain the drags! That makes it way more reliable and readable!

For example (untested code! - just to give you the idea):

$.Deferred(function(deferred) {
    $('#available-stages').children('.component-stage[component_id="2"]')
        .simulate("drag-n-drop", {
            dropTarget: $('#new-proc-parts-out'),
            callback: function() {
                setTimeout(deferred.resolve, 1000);
            }
    });
}).then(function() {
    return $.Deferred(function(deferred) {
    $('#available-stages').children('.component-stage[component_id="2"]')
        .simulate("drag-n-drop", {
            dropTarget: $('#new-proc-parts-in'),
            callback: function() {
                setTimeout(deferred.resolve, 1000);
            }
    });
   });
}).then(function() {
    // More drags...
});