2

This is the behavior I want:

  1. The user starts dragging a file from the file explorer
  2. When the file hovers over the browser window 3 drop zones appear
  3. When the user cancels the drag and drop or drops the file the drop zones disappear.

The problem I'm having is with #3.
The drop zones appear fine when using the dragenter on document but I can't get them to disappear again.

I've tried binding on dragend which never fires, dragleave which fires every time the the drag leaves a descendant so the drag area blinks.

Which event would be the correct one to listen to?

Nicklas A.
  • 6,321
  • 7
  • 39
  • 63

5 Answers5

8

I haven't fully tested it, but it seems as though you could leverage the annoying behaviour of dragenter and dragleave which fires on every single element.

var counter = 0;
$(document).on('dragenter', function () { 
  if (counter++ === 0) {
    console.log('entered the page');
  }
});

$(document).on('dragleave', function () {
  if (--counter === 0) {
    console.log('left the page');
  }
});

Seems to also work if the drag is cancelled by pressing escape.

http://jsbin.com/atodem/2/

nickf
  • 520,029
  • 197
  • 633
  • 717
  • Don't forget to add on dragover to prevent default drag&drop, besides works exactly as I want it to! – FDIM Nov 12 '13 at 14:18
  • Be careful, because this breaks easily. Check this [fiddle](http://jsfiddle.net/ueLfnLz6/3/) that shows only one of the problems. Check [my answer](http://stackoverflow.com/a/29869208/636561) for an alternative. – Luca Fagioli Apr 26 '15 at 17:37
  • Works like a charm on Chrome and Firefox, but the counter sometimes goes wrong on IE 11. – Blaž Zupančič Jul 24 '16 at 16:27
7

On document, you want to listen to both dragleave and dragover, to hide and show the zones respectively.

Tanzeeb Khalili
  • 7,214
  • 2
  • 22
  • 27
1

Well, the dragstart and dragend events appear to only fire when dragging something from the browser to the browser, which is hardly useful in your case. I couldn't seem to stop the flicker, but if you add a timeout to the dragenter and dragleave events, you can minimize the flicker:

window.onload=function(){
    var drag = new (function(){
        var timeout;
        this.detected = function(){
            return !(timeout === undefined)
        }
        this.start = function(){
            clearTimeout(timeout);
        }
        function endDrag(){
            for (var i=0;i<3;i++) //no longer dragging, remove drop zones
                document.getElementsByClassName("dropZone")[i].style.display="none";
        }
        this.end = function(){
            timeout = setTimeout((function(){timeout=undefined;endDrag();}),1500)
        }
    })()
    document.body.ondragenter = function(){
        drag.start();
        for (var i=0;i<3;i++) //drag started, show drop zones
            document.getElementsByClassName("dropZone")[i].style.display="block";
    }
    document.body.ondragleave = function(event){
        event = event||window.event
        if ((event.source||event.target)==document.body)
            drag.end();
    }
}

Hope this helps, sorry if it isn't perfect. :-(

Zaq
  • 1,288
  • 1
  • 11
  • 19
0

You can verify it by checking if drop has been called before dragend.

     var dragConfirmed;

     document.addEventListener("drop", function( event ) {
          // prevent default action (open as link for some elements)
          event.preventDefault();

          dragConfirmed = true;
      }, false);



      document.addEventListener("dragend", function( event ) {

        if (dragConfirmed != true)
        {
             // Escape has been pressed
        }

      }, false);
0

dragleave didn't work for me. I used a timeout:

function stopDrag(){
   console.log('bye drag!');
}

var timeoutHandle;
document.addEventListener('dragover', function(){
    console.log('you are dragging');
    window.clearTimeout(timeoutHandle);
    timeoutHandle = window.setTimeout(stopDrag, 200);
}, false);
Aureliano Far Suau
  • 6,291
  • 2
  • 20
  • 25