How do I detect a HTML5 drag event entering and leaving the window, like Gmail does?

后端 未结 12 903
醉话见心
醉话见心 2020-12-13 20:16

I\'d like to be able to highlight the drop area as soon as the cursor carrying a file enters the browser window, exactly the way Gmail does it. But I can\'t make it work, an

相关标签:
12条回答
  • 2020-12-13 20:20

    When the file enters and leaves child elements it fires additional dragenter and dragleave so you need to count up and down.

    var count = 0
    
    document.addEventListener("dragenter", function() {
        if (count === 0) {
            setActive()
        }
        count++
    })
    
    document.addEventListener("dragleave", function() {
        count--
        if (count === 0) {
            setInactive()
        }
    })
    
    document.addEventListener("drop", function() {
        if (count > 0) {
            setInactive()
        }
        count = 0
    })
    
    0 讨论(0)
  • 2020-12-13 20:28

    @tyler's answer is the best! I have upvoted it. After spending so many hours I got that suggestion working exactly as intended.

    $(document).on('dragstart dragenter dragover', function(event) {    
        // Only file drag-n-drops allowed, http://jsfiddle.net/guYWx/16/
        if ($.inArray('Files', event.originalEvent.dataTransfer.types) > -1) {
            // Needed to allow effectAllowed, dropEffect to take effect
            event.stopPropagation();
            // Needed to allow effectAllowed, dropEffect to take effect
            event.preventDefault();
    
            $('.dropzone').addClass('dropzone-hilight').show();     // Hilight the drop zone
            dropZoneVisible= true;
    
            // http://www.html5rocks.com/en/tutorials/dnd/basics/
            // http://api.jquery.com/category/events/event-object/
            event.originalEvent.dataTransfer.effectAllowed= 'none';
            event.originalEvent.dataTransfer.dropEffect= 'none';
    
             // .dropzone .message
            if($(event.target).hasClass('dropzone') || $(event.target).hasClass('message')) {
                event.originalEvent.dataTransfer.effectAllowed= 'copyMove';
                event.originalEvent.dataTransfer.dropEffect= 'move';
            } 
        }
    }).on('drop dragleave dragend', function (event) {  
        dropZoneVisible= false;
    
        clearTimeout(dropZoneTimer);
        dropZoneTimer= setTimeout( function(){
            if( !dropZoneVisible ) {
                $('.dropzone').hide().removeClass('dropzone-hilight'); 
            }
        }, dropZoneHideDelay); // dropZoneHideDelay= 70, but anything above 50 is better
    });
    
    0 讨论(0)
  • 2020-12-13 20:29

    I solved it with a timeout (not squeaky-clean, but works):

    var dropTarget = $('.dropTarget'),
        html = $('html'),
        showDrag = false,
        timeout = -1;
    
    html.bind('dragenter', function () {
        dropTarget.addClass('dragging');
        showDrag = true; 
    });
    html.bind('dragover', function(){
        showDrag = true; 
    });
    html.bind('dragleave', function (e) {
        showDrag = false; 
        clearTimeout( timeout );
        timeout = setTimeout( function(){
            if( !showDrag ){ dropTarget.removeClass('dragging'); }
        }, 200 );
    });
    

    My example uses jQuery, but it's not necessary. Here's a summary of what's going on:

    • Set a flag (showDrag) to true on dragenter and dragover of the html (or body) element.
    • On dragleave set the flag to false. Then set a brief timeout to check if the flag is still false.
    • Ideally, keep track of the timeout and clear it before setting the next one.

    This way, each dragleave event gives the DOM enough time for a new dragover event to reset the flag. The real, final dragleave that we care about will see that the flag is still false.

    0 讨论(0)
  • 2020-12-13 20:31

    Don't know it this works for all cases but in my case it worked very well

    $('body').bind("dragleave", function(e) {
       if (!e.originalEvent.clientX && !e.originalEvent.clientY) {
          //outside body / window
       }
    });

    0 讨论(0)
  • 2020-12-13 20:31

    Have you noticed that there is a delay before the dropzone disappears in Gmail? My guess is that they have it disappear on a timer (~500ms) that gets reset by dragover or some such event.

    The core of the problem you described is that dragleave is triggered even when you drag into a child element. I'm trying to find a way to detect this, but I don't have an elegantly clean solution yet.

    0 讨论(0)
  • 2020-12-13 20:36

    I was having trouble with this myself and came up with a usable solution, though I'm not crazy about having to use an overlay.

    Add ondragover, ondragleave and ondrop to window

    Add ondragenter, ondragleave and ondrop to an overlay and a target element

    If drop occurs on the window or overlay, it is ignored, whereas the target handles the drop as desired. The reason we need an overlay is because ondragleave triggers every time an element is hovered, so the overlay prevents that from happening, while the drop zone is given a higher z-index so that the files can be dropped. I am using some code snippets found in other drag and drop related questions, so I cannot take full credit. Here's the full HTML:

    <!DOCTYPE html>
    <html>
        <head>
            <title>Drag and Drop Test</title>
            <meta http-equiv="X-UA-Compatible" content="chrome=1" />
            <style>
            #overlay {
                display: none;
                left: 0;
                position: absolute;
                top: 0;
                z-index: 100;
            }
            #drop-zone {
                background-color: #e0e9f1;
                display: none;
                font-size: 2em;
                padding: 10px 0;
                position: relative;
                text-align: center;
                z-index: 150;
            }
            #drop-zone.hover {
                background-color: #b1c9dd;
            }
            output {
                bottom: 10px;
                left: 10px;
                position: absolute;
            }
            </style>
            <script>
                var windowInitialized = false;
                var overlayInitialized = false;
                var dropZoneInitialized = false;
    
                function handleFileSelect(e) {
                    e.preventDefault();
    
                    var files = e.dataTransfer.files;
                    var output = [];
    
                    for (var i = 0; i < files.length; i++) {
                        output.push('<li>',
                            '<strong>', escape(files[i].name), '</strong> (', files[i].type || 'n/a', ') - ',
                            files[i].size, ' bytes, last modified: ',
                            files[i].lastModifiedDate ? files[i].lastModifiedDate.toLocaleDateString() : 'n/a',
                            '</li>');
                    }
    
                    document.getElementById('list').innerHTML = '<ul>' + output.join('') + '</ul>';
                }
    
                window.onload = function () {
                    var overlay = document.getElementById('overlay');
                    var dropZone = document.getElementById('drop-zone');
    
                    dropZone.ondragenter = function () {
                        dropZoneInitialized = true;
                        dropZone.className = 'hover';
                    };
                    dropZone.ondragleave = function () {
                        dropZoneInitialized = false;
                        dropZone.className = '';
                    };
                    dropZone.ondrop = function (e) {
                        handleFileSelect(e);
                        dropZoneInitialized = false;
                        dropZone.className = '';
                    };
    
                    overlay.style.width = (window.innerWidth || document.body.clientWidth) + 'px';
                    overlay.style.height = (window.innerHeight || document.body.clientHeight) + 'px';
                    overlay.ondragenter = function () {
                        if (overlayInitialized) {
                            return;
                        }
    
                        overlayInitialized = true;
                    };
                    overlay.ondragleave = function () {
                        if (!dropZoneInitialized) {
                            dropZone.style.display = 'none';
                        }
                        overlayInitialized = false;
                    };
                    overlay.ondrop = function (e) {
                        e.preventDefault();
                        dropZone.style.display = 'none';
                    };
    
                    window.ondragover = function (e) {
                        e.preventDefault();
    
                        if (windowInitialized) {
                            return;
                        }
    
                        windowInitialized = true;
                        overlay.style.display = 'block';
                        dropZone.style.display = 'block';
                    };
                    window.ondragleave = function () {
                        if (!overlayInitialized && !dropZoneInitialized) {
                            windowInitialized = false;
                            overlay.style.display = 'none';
                            dropZone.style.display = 'none';
                        }
                    };
                    window.ondrop = function (e) {
                        e.preventDefault();
    
                        windowInitialized = false;
                        overlayInitialized = false;
                        dropZoneInitialized = false;
    
                        overlay.style.display = 'none';
                        dropZone.style.display = 'none';
                    };
                };
            </script>
        </head>
    
        <body>
            <div id="overlay"></div>
            <div id="drop-zone">Drop files here</div>
            <output id="list"><output>
        </body>
    </html>
    
    0 讨论(0)
提交回复
热议问题