Verhindern Sie, dass der Browser eine Drag & Drop-Datei lädt

194

Ich füge meiner Seite einen HTML5-Drag & Drop-Uploader hinzu.

Wenn eine Datei in den Upload-Bereich verschoben wird, funktioniert alles einwandfrei.

Wenn ich die Datei jedoch versehentlich außerhalb des Upload-Bereichs ablege, lädt der Browser die lokale Datei so, als wäre es eine neue Seite.

Wie kann ich dieses Verhalten verhindern?

Vielen Dank!

Travis
quelle
2
Nur neugierig, welchen Code Sie für das Hochladen von HTML5 per Drag & Drop verwenden. Vielen Dank.
Robertwbradford
Das Problem, das Sie haben, wird entweder durch das Fehlen von e.dataTransfer () oder durch das Fehlen eines PreventDefault () in drop / dragenter / etc. verursacht. Veranstaltungen. Aber ich kann es nicht ohne ein Codebeispiel sagen.
HoldOffHunger

Antworten:

312

Sie können dem Fenster einen Ereignis-Listener hinzufügen, der preventDefault()alle Dragover- und Drop-Ereignisse aufruft.
Beispiel:

window.addEventListener("dragover",function(e){
  e = e || event;
  e.preventDefault();
},false);
window.addEventListener("drop",function(e){
  e = e || event;
  e.preventDefault();
},false);
Digitales Flugzeug
quelle
44
Dragover ist das Stück, das mir gefehlt hat.
Cgatian
11
Ich bestätige, dass sowohl dragoverals auch dropHandler erforderlich sind, um zu verhindern, dass der Browser die abgelegte Datei lädt. (Chrome spätestens 03.08.2015). Die Lösung funktioniert auch mit FF.
Offirmo
4
Dies funktioniert einwandfrei und ich kann bestätigen, dass es in Kombination mit Seitenelementen verwendet werden kann, die so konfiguriert sind, dass sie Drop-Ereignisse akzeptieren, z. B. solche aus Drag & Drop-Datei-Upload-Skripten wie resumable.js. Es ist nützlich, das Standardverhalten des Browsers zu verhindern, wenn ein Benutzer versehentlich eine Datei, die er hochladen möchte, außerhalb der eigentlichen Drop-Zone für das Hochladen von Dateien ablegt und sich dann fragt, warum dieselbe Datei jetzt direkt im Browserfenster gerendert wird ( Angenommen, ein kompatibler Dateityp wie ein Bild oder Video wurde gelöscht) und nicht das erwartete Verhalten beim Hochladen der Datei.
Bluebinary
15
Hinweis: Dadurch wird auch das Ziehen von Dateien auf a deaktiviert <input type="file" />. Es ist zu überprüfen, ob e.targetes sich um eine Dateieingabe handelt, und solche Ereignisse durchzulassen.
Sebastian Nowak
6
was? Warum sollte Windows Dragover die Datei laden? das macht keinen Sinn ...
L.Trabacchin
37

Nachdem ich viel herumgespielt hatte, fand ich, dass dies die stabilste Lösung ist:

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>

Wenn Sie beide effectAllowund dropEffectbedingungslos im Fenster festlegen, akzeptiert meine Drop-Zone kein dnd mehr, unabhängig davon, ob die Eigenschaften neu festgelegt sind oder nicht.

Axel Amthor
quelle
e.dataTransfer () ist hier das entscheidende Element, das diese Arbeit ermöglicht, das in der "akzeptierten Antwort" nicht erwähnt wurde.
HoldOffHunger
9

Für jQuery lautet die richtige Antwort:

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

Hier return falseverhält es sich wie event.preventDefault()und event.stopPropagation().

Vision
quelle
9

Um Drag & Drop nur für einige Elemente zuzulassen, können Sie Folgendes tun:

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);
Enthusiast
quelle
Funktioniert perfekt für mich, aber ich würde auch die Prüfung für type = file hinzufügen, sonst können Sie immer noch auf Texteingaben ziehen
Andreas Zwerger
2

Versuche dies:

document.body.addEventListener('drop', function(e) {
    e.preventDefault();
}, false);
moe
quelle
2

Das standardmäßige Verhindern aller Drag & Drop-Vorgänge ist möglicherweise nicht das, was Sie möchten. Zumindest in einigen Browsern kann überprüft werden, ob es sich bei der Drag-Quelle um eine externe Datei handelt. Ich habe eine Funktion zum Überprüfen, ob die Drag-Quelle eine externe Datei ist, in diese StackOverflow-Antwort aufgenommen .

Wenn Sie die Antwort von Digital Plane ändern, können Sie Folgendes tun:

function isDragSourceExternalFile() {
     // Defined here: 
     // https://stackoverflow.com/a/32044172/395461
}

window.addEventListener("dragover",function(e){
    e = e || event;
    var IsFile = isDragSourceExternalFile(e.originalEvent.dataTransfer);
    if (IsFile) e.preventDefault();
},false);
window.addEventListener("drop",function(e){
    e = e || event;
    var IsFile = isDragSourceExternalFile(e.originalEvent.dataTransfer);
    if (IsFile) e.preventDefault();
},false);
Shannon Matthews
quelle
1
Was ist der Sinn von e || event;? Wo ist eventdefiniert? Keine Ursache. Es sieht so aus, als wäre es ein globales Objekt im IE? Ich fand dieses Zitat "In Microsoft Visual Basic Scripting Edition (VBScript), you must access the event object through the window object." hier
1,21 Gigawatt
2

Hinweis: Obwohl das OP nicht nach einer Angular-Lösung gefragt hat, bin ich hierher gekommen, um danach zu suchen. Dies ist also, um zu teilen, was ich als praktikable Lösung empfunden habe, wenn Sie Angular verwenden.

Nach meiner Erfahrung tritt dieses Problem zuerst auf, wenn Sie einer Seite Funktionen zum Löschen von Dateien hinzufügen. Daher ist meine Meinung, dass die Komponente, die dies hinzufügt, auch dafür verantwortlich sein sollte, ein Abfallen außerhalb der Abwurfzone zu verhindern.

In meiner Lösung ist die Drop-Zone eine Eingabe mit einer Klasse, aber jeder eindeutige Selektor funktioniert.

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';
    }
  }
}

Die Listener werden automatisch hinzugefügt / entfernt, wenn eine Komponente erstellt / zerstört wird, und andere Komponenten, die dieselbe Strategie auf derselben Seite verwenden, stören sich aufgrund von stopPropagation () nicht gegenseitig.

Superole
quelle
Das funktioniert wie ein Zauber !! Der Browser ändert sogar den Mauszeiger, indem er ein Verbotssymbol hinzufügt, das so großartig ist !!
pti_jul
1

Um auf der in einigen anderen Antworten beschriebenen Methode "Ziel überprüfen" aufzubauen, finden Sie hier eine allgemeinere / funktionalere Methode:

function preventDefaultExcept(predicates) {
  return function (e) {
    var passEvery = predicates.every(function (predicate) { return predicate(e); })
    if (!passEvery) {
      e.preventDefault();
    }
  };
}

Genannt wie:

function isDropzone(e) { return e.target.id === 'dropzone'; }
function isntParagraph(e) { return e.target.tagName !== 'p'; }

window.addEventListener(
  'dragover',
  preventDefaultExcept([isDropzone, isntParagraph])
);
window.addEventListener(
  'drop',
  preventDefaultExcept([isDropzone])
);
scott_trinh
quelle
Könnte hier auch etwas ES6 hinzufügen : function preventDefaultExcept(...predicates){}. Und dann benutze es wiepreventDefaultExcept(isDropzone, isntParagraph)
hlfrmn
0

Ich habe ein HTML object( embed), das die Breite und Höhe der Seite ausfüllt. Die Antwort von @ digital-plane funktioniert auf normalen Webseiten, jedoch nicht, wenn der Benutzer auf ein eingebettetes Objekt fällt. Also brauchte ich eine andere Lösung.

Wenn wir zur Ereigniserfassungsphase wechseln, können wir truedie Ereignisse abrufen, bevor das eingebettete Objekt sie empfängt (beachten Sie den Wert am Ende des Aufrufs des Ereignis-Listeners):

// document.body or window
document.body.addEventListener("dragover", function(e){
  e = e || event;
  e.preventDefault();
  console.log("over true");
}, true);

document.body.addEventListener("drop", function(e){
  e = e || event;
  e.preventDefault();
  console.log("drop true");
}, true);

Mit dem folgenden Code (basierend auf der Antwort von @ digital-plane) wird die Seite zu einem Ziehziel. Sie verhindert, dass Objekteinbettungen die Ereignisse erfassen, und lädt dann unsere Bilder:

document.body.addEventListener("dragover", function(e){
  e = e || event;
  e.preventDefault();
  console.log("over true");
}, true);

document.body.addEventListener("drop",function(e){
  e = e || event;
  e.preventDefault();
  console.log("Drop true");

  // begin loading image data to pass to our embed
  var droppedFiles = e.dataTransfer.files;
  var fileReaders = {};
  var files = {};
  var reader;

  for (var i = 0; i < droppedFiles.length; i++) {
    files[i] = droppedFiles[i]; // bc file is ref is overwritten
    console.log("File: " + files[i].name + " " + files[i].size);
    reader = new FileReader();
    reader.file = files[i]; // bc loadend event has no file ref

    reader.addEventListener("loadend", function (ev, loadedFile) {
      var fileObject = {};
      var currentReader = ev.target;

      loadedFile = currentReader.file;
      console.log("File loaded:" + loadedFile.name);
      fileObject.dataURI = currentReader.result;
      fileObject.name = loadedFile.name;
      fileObject.type = loadedFile.type;
      // call function on embed and pass file object
    });

    reader.readAsDataURL(files[i]);
  }

}, true);

Getestet auf Firefox auf Mac.

1,21 Gigawatt
quelle
0

Ich verwende eine Klassenauswahl für mehrere Upload-Bereiche, daher hat meine Lösung diese weniger reine Form angenommen

Basierend auf der Antwort von Axel Amthor, abhängig von jQuery (Alias ​​auf $)

_stopBrowserFromOpeningDragAndDropPDFFiles = function () {

        _preventDND = function(e) {
            if (!$(e.target).is($(_uploadBoxSelector))) {
                e.preventDefault();
                e.dataTransfer.effectAllowed = 'none';
                e.dataTransfer.dropEffect = 'none';
            }
        };

        window.addEventListener('dragenter', function (e) {
            _preventDND(e);
        }, false);

        window.addEventListener('dragover', function (e) {
            _preventDND(e);
        });

        window.addEventListener('drop', function (e) {
            _preventDND(e);
        });
    },
hngr18
quelle