I’m trying to create a file drag/drop handler (drag a file into the browser window, to be used for upload).
For some reason when I bind the drag/drop listener to $("body") instead of to a $("div") in the body the events fire several times in a row, sometimes even non-stop (seemingly looping). What could be causing this?
Here’s a trimmed down version of the code: http://jsfiddle.net/WxMwK/9/
var over = false;
$("body")
.on("dragover", function(e){
e.preventDefault();
if (! over) {
over = true;
$("ul").append($("<li/>").text("dragover"));
}
})
.on("dragleave", function(e){
e.preventDefault();
if (over) {
over = false;
$("ul").append($("<li/>").text("dragleave"));
}
})
.on("drop", function(e){
e.preventDefault();
if (over) {
over = false;
$("ul").append($("<li/>").text("drop"));
}
});
To test: drag a file into the orange area, you’ll see the event firing multiple times in a row.
The anon is (mostly) correct. To put it simply: when the mouse moves over the edge of an element inside your drop target, you get a
dropenterfor the element under the cursor and adropleavefor the element that was under the cursor previously. This happens for absolutely any descendant.You can’t check the element associated with
dragleave, because if you move the mouse from your drop target onto a child element, you’ll get adropenterfor the child and then adropleavefor the target! It’s kind of ridiculous and I don’t see how this is a useful design at all.Here’s a crappy jQuery-based solution I came up with some time ago.
The trick relies on two facts:
dragenteranddragleavewill be queued up for the target element—in that order.dragenteranddragleaveare queued together.So here’s what happens.
dragenterevent, I set some shared variable to indicate that the drag movement hasn’t finished resolving yet.setTimeoutwith a delay of zero to immediately change that variable back.dragleave‘s event handler.dragleavesees that it was paired with adragenteron the same target element, that means the mouse must have moved from some descendant to some other descendant. Otherwise, the mouse is actually leaving the target element.setTimeoutfinally resolves zero seconds later, setting back the variable before another event can come along.I can’t think of a simpler approach.