Can't get drop event to fire (and yes, I preventDefault()ed in dragover)

666 views Asked by At

I'm working on an implementation of draggable HTML5 lists and running into trouble.

Basically, whenever I drag over an element, I insert the element I'm dragging immediately before or after the element I just entered. This means that as you move the mouse around, the element keeps moving into position under the mouse. (I set visibility: hidden on the element, so it appears as a blank space.) When you drop the element, I then send an AJAX call to the server describing the element's position to execute the move on the server.

The problem is, for whatever reason, the drop event never fires (a breakpoint in my handler is never hit), and the drag image snaps back to wherever it started. (Of course, the element has moved on the page, so it reappears in its new location, but since drop never fires, the server is never notified of the change.) Does anyone know what's going on? The usual answer is "you need to call preventDefault() in dragenter/dragover", but I'm already doing that.

I'm using jQuery 1.10.2, and I'm seeing this behavior in both Safari 7.0.1 and Firefox 26.0. For what it's worth, Rails 4.0.2 is my framework for this project, and I'm using jquery_ujs and turbolinks.

Relevant code:

var draggingLI;

$('ol[data-passage-id]').find('li > a').attr('draggable', 'true')
.on('dragstart', function (ev) {
    draggingLI = $(ev.target).closest("li")[0];

    ev.originalEvent.dataTransfer.effectAllowed = 'move';
    ev.originalEvent.dataTransfer.setData('text/plain', ev.target.href);
    ev.originalEvent.dataTransfer.setData('text/uri-list', ev.target.href);
})
.on('dragenter', function (ev) {
    ev.originalEvent.dataTransfer.dropEffect = 'move'; 
    $(draggingLI).css('visibility', 'hidden');

    if(canDropIn(ev.target)) {
        var droppingLI = $(ev.target).closest("li")[0];

        if(draggingLI == droppingLI) {
            // do nothing
        }
        if(draggingLI.nextElementSibling == droppingLI) {
            // We want to move the dragging element below this element.
            $(droppingLI).after(draggingLI);
        }
        else {
            // We want to move the dragging element above this element.
            $(droppingLI).before(draggingLI);
        }

        ev.preventDefault();
        ev.stopPropagation();
        return false;
    }
})
.on('dragover', function (ev) {
    if(canDropIn(ev.target)) {
        ev.preventDefault();
        ev.stopPropagation();
        return false;
    }
})
.on('drop', function (ev) {
    sendMovedItem(draggingLI);
    ev.preventDefault();
    ev.stopPropagation();
    return false;
})
.on('dragend', function (ev) {
    $(draggingLI).css('visibility', '');
});

canDropIn() is currently a stub that always returns true. sendMovedItem() is the function that does the AJAX call. There are no errors on the console.

1

There are 1 answers

0
Becca Royal-Gordon On BEST ANSWER

The problem seems to be caused by my strategy of moving the drag element itself around; I'm not sure if it's because moving the element while it's being dragged causes browsers trouble, or if it's because dropping on the original element isn't allowed. I worked around it by hiding the original element entirely and then inserting a placeholder element I could move around instead. (You have to attach dragenter/dragover/drop handlers to the placeholder, since all successful drops end on the placeholder.)

I'd still prefer a solution that didn't require the placeholder, though, if anyone has an idea!