7

Issue

I've just finished implementing a feature in which users can drag files into the browser and drop upon a supporting File Upload plugin, which processes the drop.

However, in order to give users a hint that they can even drop things in the first place, I've implemented a dragover event to show a div that says something akin to "Drop Here". This, in turn, hides the div which has the "Choose File..." button, and replaces it, until the user stops dragging.

But it seems, when I implement this, dragging over the target area is causing flickering. To be clear:

  • div with "Choose File" interface is shown.
  • Item dragged (or selected text dragged); "Drop here" is shown.
  • Item dragged over the "Drop here" area; flickering begins.

Additionally:

  • There is no issue in Opera 12 or Firefox 16.
  • The issue is blatantly obvious in Chrome 23 and Safari 5.
  • The issue is partly there in IE 9 (IE 10 untested); it flickers for about 5 seconds, then stops.

jsFiddle

(Warning: the fiddle is very crude.)

Just select some of the text and drag it over the blue box and you will see what happens; it will be obvious the behavior it should not be exhibiting.

Code used in fiddle

var $dropTarget = $("#container");
$(document).bind("dragover", function(e) {
    if ($dropTarget.hasClass("highlight"))
        return;

    $dropTarget.addClass("highlight");
    $dropTarget.find("[name='drop']").show();
    $dropTarget.find("[name='drag']").hide();
}).bind("dragleave drop", function(e) {
    if (!$dropTarget.hasClass("highlight"))
        return;

    $dropTarget.removeClass("highlight");
    $dropTarget.find("[name='drop']").hide();
    $dropTarget.find("[name='drag']").show();
});​

My solution...?

To be honest, I've got no idea of what to try. There is not extensive documentation on the behavior of dragover or dragleave, and I don't even know why this is happening, so I can't even begin to debug it. I feel like dragover should only fire once, but even dragging around the screen just fires it over and over and over again.

I've looked at Google Images and Google Contacts for their drag-and-drop behavior, but their code is completely minified and unreadable, and I can't even find any specified "drag" behavior.

So, is there some fix to this seemingly odd behavior? If this is a bug in WebKit, which I suspect, is there some brilliant workaround and/or hack I can use?

Thanks to everyone for their time!

Cat
  • 66,919
  • 24
  • 133
  • 141

4 Answers4

28

After over an hour of scouring, I found someone who had a similar type of issue. It seems that Chrome and Safari (5, at least) fire dragleave upon entering a child element (and seemingly, upon any changes to that element, including the children being shown/hidden).

The solution is to check if pageX and pageY are equal to 0 within dragleave (but not drop).

var $dropTarget = $("#container");
$(document).bind("dragenter", function(e) {
    if (e.target == this) {
         return;
    }
                
    $dropTarget.addClass("highlight");
    $dropTarget.find("[name='drop']").show();
    $dropTarget.find("[name='drag']").hide();
}).bind("dragleave", function(e) {
    if (e.originalEvent.pageX != 0 || e.originalEvent.pageY != 0) {
        return false;
    }
    
    // Could use .trigger("drop") here.
    $dropTarget.removeClass("highlight");
    $dropTarget.find("[name='drop']").hide();
    $dropTarget.find("[name='drag']").show();
}).bind("drop", function(e) {
    $dropTarget.removeClass("highlight");
    $dropTarget.find("[name='drop']").hide();
    $dropTarget.find("[name='drag']").show();
});​
Junaid
  • 4,682
  • 1
  • 34
  • 40
Cat
  • 66,919
  • 24
  • 133
  • 141
2

i have some project using drag and drop file and was found same issue on my project, this is solution for your issue and tested on your case and thats is work 100%

this is the magic code :

<style type="text/css">
    .drag * {
        pointer-events: none;
    }
</style>

<div class="drag" id='container'>
<div name='drop' style='display: none;'>DROP HERE</div>
<div name='drag'>DRAG HERE</div>
</div> 
Damar
  • 21
  • 1
0
$(window).bind('dragover', '#dragdrop', function(e) {
        e.preventDefault();
        e.stopPropagation();
        $('#dragdrop').addClass('show');

    }).bind('dragleave drop', '#dragdrop', function(e) {
        e.preventDefault();
        e.stopPropagation();
        if ((e.originalEvent.pageX != 0 || e.originalEvent.pageY != 0) && e.originalEvent.dataTransfer.files.length === 0) {
            return false;
        }

        $('#dragdrop').removeClass('show');

        if (e.originalEvent.dataTransfer) {
            if(e.originalEvent.dataTransfer.files.length) {
                $('#dragdrop').removeClass('show');
                var files = e.originalEvent.dataTransfer.files
                $('#files').prop('files', files);
            }
        }
    })
Ehson
  • 1
  • 1
    You should really consider adding some comments or explanation to your answer, so that OP can have a better idea of what you are sharing here. – Miguel Aug 05 '22 at 13:45
-1

I found that with Eric's solution if I dragged off of the screen on Win10 Google Chrome (on to another window) after using this solution, it didn't work for me. Changing the conditional to an AND worked instead.

var $dropTarget = $("#container");
$(document).bind("dragenter", function(e) {
    if (e.target == this) {
         return;
    }

    $dropTarget.addClass("highlight");
    $dropTarget.find("[name='drop']").show();
    $dropTarget.find("[name='drag']").hide();
}).bind("dragleave", function(e) {
    if (e.originalEvent.pageX != 0 || e.originalEvent.pageY != 0) {
        return false;
    }

    // Could use .trigger("drop") here.
    $dropTarget.removeClass("highlight");
    $dropTarget.find("[name='drop']").hide();
    $dropTarget.find("[name='drag']").show();
}).bind("drop", function(e) {
    $dropTarget.removeClass("highlight");
    $dropTarget.find("[name='drop']").hide();
    $dropTarget.find("[name='drag']").show();
});​
Junaid
  • 4,682
  • 1
  • 34
  • 40
Jordan Yim
  • 59
  • 1
  • 3
  • You can select your response, control find, and highlight his entire answer. This should be removed - it is an exact copy of his answer. If you believe there is a conditional fix, please modify your response to reflect that. – iSkore Oct 20 '17 at 22:07