Prevent browser from loading a drag-and-dropped file

86,470

Solution 1

You can add a event listener to the window that calls preventDefault() on all dragover and drop events.
Example:

window.addEventListener("dragover",function(e){
  e = e || event;
  e.preventDefault();
},false);
window.addEventListener("drop",function(e){
  e = e || event;
  e.preventDefault();
},false);

Solution 2

After a lot of fiddling around, I found this to be the stablest solution:

var dropzoneId = "dropzone";

window.addEventListener("dragenter", function(e) {
  if (e.target.id != dropzoneId) {
    e.preventDefault();
    e.dataTransfer.effectAllowed = "none";
    e.dataTransfer.dropEffect = "none";
  }
}, false);

window.addEventListener("dragover", function(e) {
  if (e.target.id != dropzoneId) {
    e.preventDefault();
    e.dataTransfer.effectAllowed = "none";
    e.dataTransfer.dropEffect = "none";
  }
});

window.addEventListener("drop", function(e) {
  if (e.target.id != dropzoneId) {
    e.preventDefault();
    e.dataTransfer.effectAllowed = "none";
    e.dataTransfer.dropEffect = "none";
  }
});
<div id="dropzone">...</div>

Setting both effectAllow and dropEffect unconditionally on the window causes my drop zone not to accept any d-n-d any longer, regardless whether the properties are set new or not.

Solution 3

For jQuery the correct answer will be:

$(document).on({
    dragover: function() {
        return false;
    },
    drop: function() {
        return false;
    }
});

Here return false will behave as event.preventDefault() and event.stopPropagation().

Solution 4

To allow drag-and-drop only on some elements, you could do something like:

window.addEventListener("dragover",function(e){
  e = e || event;
  console.log(e);
  if (e.target.tagName != "INPUT") { // check which element is our target
    e.preventDefault();
  }
},false);
window.addEventListener("drop",function(e){
  e = e || event;
  console.log(e);
  if (e.target.tagName != "INPUT") {  // check which element is our target
    e.preventDefault();
  }  
},false);

Solution 5

Note: Although the OP did not ask for an Angular solution, I came here looking for that. So this is to share what I found to be a viable solution, if you use Angular.

In my experience this problem first arises when you add file drop functionality to a page. Therefore my opinion is that the component that adds this, should also be responsible for preventing drop outside of the drop zone.

In my solution the drop zone is an input with a class, but any unambiguous selector works.

import { Component, HostListener } from '@angular/core';
//...

@Component({
  template: `
    <form>
      <!-- ... -->
      <input type="file" class="dropzone" />
    </form>
  `
})
export class MyComponentWithDropTarget {

  //...

  @HostListener('document:dragover', ['$event'])
  @HostListener('drop', ['$event'])
  onDragDropFileVerifyZone(event) {
    if (event.target.matches('input.dropzone')) {
      // In drop zone. I don't want listeners later in event-chain to meddle in here
      event.stopPropagation();
    } else {
      // Outside of drop zone! Prevent default action, and do not show copy/move icon
      event.preventDefault();
      event.dataTransfer.effectAllowed = 'none';
      event.dataTransfer.dropEffect = 'none';
    }
  }
}

The listeners are added/removed automatically when component is created/destroyed, and other components using the same strategy on the same page do not interfere with each other due to the stopPropagation().

Share:
86,470

Related videos on Youtube

Travis
Author by

Travis

Updated on September 26, 2020

Comments

  • Travis
    Travis over 3 years

    I'm adding an html5 drag and drop uploader to my page.

    When a file is dropped into the upload area, everything works great.

    However, if I accidentally drop the file outside of the upload area, the browser loads the local file as if it is a new page.

    How can I prevent this behavior?

    Thanks!

    • robertwbradford
      robertwbradford almost 13 years
      Just curious what code you are using to handle the html5 drag/drop uploading. Thanks.
    • HoldOffHunger
      HoldOffHunger about 6 years
      The problem you have is caused by either missing e.dataTransfer() or missing a preventDefault() on drop/dragenter/etc. events. But I can't tell without a code sample.
  • cgatian
    cgatian over 10 years
    dragover is the piece I was missing.
  • Offirmo
    Offirmo almost 9 years
    I confirm that both dragover and drop handlers are needed to prevent the browser from loading the dropped file. (Chrome latest 2015/08/03). The solution works on FF latest, too.
  • bluebinary
    bluebinary almost 8 years
    This works perfectly, and I can confirm that it can be used in combination with page elements that are configured to accept drop events, such as those from drag-and-drop file upload scrips like resumable.js. It is useful to prevent the default browser behavior in cases where a user accidentally drops a file they want to upload outside of the actual file-upload drop-zone, and then wonders why they now see that same file rendered directly in the browser window (assuming a compatible file type like an image or video was dropped), rather than the expected behavior of seeing their file upload.
  • Sebastian Nowak
    Sebastian Nowak over 7 years
    Note: this also disables dragging files onto a <input type="file" />. It is necessary to check whether e.target is a file input and let such events through.
  • 1.21 gigawatts
    1.21 gigawatts over 7 years
    What's the point of e || event;? Where is event defined? Nevermind. It looks like it's a global object in IE? I found this quote, "In Microsoft Visual Basic Scripting Edition (VBScript), you must access the event object through the window object." here
  • L.Trabacchin
    L.Trabacchin over 6 years
    what ? why should window dragover load the file ? this makes no sense ...
  • HoldOffHunger
    HoldOffHunger about 6 years
    e.dataTransfer() is the critical piece here that makes this work, which the "accepted answer" failed to mention.
  • Marian
    Marian almost 6 years
    Also, could add some ES6 here: function preventDefaultExcept(...predicates){}. And then use it like preventDefaultExcept(isDropzone, isntParagraph)
  • AbbasFaisal
    AbbasFaisal over 5 years
    By any means is it also possible to not show move/copy icon?
  • Peter Moore
    Peter Moore over 4 years
    You have to add e.dataTransfer.dropEffect = "none"; to prevent the visual feedback.
  • pti_jul
    pti_jul over 4 years
    This works like a charm !! The browser even change the mouse cursor by adding a ban icon which is so great !!
  • Andreas Zwerger
    Andreas Zwerger about 4 years
    Works perfect for me, but i would also add check for type=file, otherwise you still can drag to text inputs
  • AJ Richardson
    AJ Richardson over 3 years
    Instead of checking e.target.id, you could call event.stopPropagation() from the drop zone's event handlers. Also, it is not necessary to set effectedAlled here as @HoldOffHunger alluded to.
  • Nilpo
    Nilpo over 3 years
    What's the need for e || event? Shouldn't e always contain the event?
  • Nilpo
    Nilpo over 3 years
    FWIW, this solution also disables drag and drop on all child elements even if they have listeners explicitly set in Chrome 86.
  • BenKoshy
    BenKoshy over 3 years
    @Nilpo how did you solve the problem of child elements with explict listeners?
  • johannes
    johannes almost 3 years
    "Therefore my opinion is that the component that adds this, should also be responsible for preventing drop outside of the drop zone." Good point!
  • Nilpo
    Nilpo almost 2 years
    I've added this answer with more modern ES6 syntax.