Mencegah browser memuat file seret-dan-jatuhkan

194

Saya menambahkan html5 drag and drop uploader ke halaman saya.

Saat file dijatuhkan ke area unggahan, semuanya berfungsi dengan baik.

Namun, jika saya secara tidak sengaja menjatuhkan file di luar area unggahan, browser memuat file lokal seolah-olah itu adalah halaman baru.

Bagaimana saya bisa mencegah perilaku ini?

Terima kasih!

Travis
sumber
2
Hanya ingin tahu kode apa yang Anda gunakan untuk menangani pengunggahan html5 drag / drop. Terima kasih.
robertwbradford
Masalah yang Anda miliki disebabkan oleh hilangnya e.dataTransfer () atau hilang preventDefault () pada drop / dragenter / etc. acara Tapi saya tidak bisa mengatakannya tanpa contoh kode.
HoldOffHunger

Jawaban:

312

Anda dapat menambahkan pendengar acara ke jendela yang memanggil preventDefault()semua aktivitas dragover dan drop.
Contoh:

window.addEventListener("dragover",function(e){
  e = e || event;
  e.preventDefault();
},false);
window.addEventListener("drop",function(e){
  e = e || event;
  e.preventDefault();
},false);
Pesawat Digital
sumber
44
dragover adalah bagian yang hilang.
cgatian
11
Saya mengkonfirmasi bahwa keduanya dragoverdan droppenangan diperlukan untuk mencegah browser memuat file yang dijatuhkan. (Chrome terbaru 2015/08/03). Solusinya bekerja pada FF terbaru juga.
Offirmo
4
Ini berfungsi dengan baik, dan saya dapat mengonfirmasi bahwa itu dapat digunakan dalam kombinasi dengan elemen halaman yang dikonfigurasikan untuk menerima peristiwa drop, seperti yang dari skrip pengunggahan file drag-and-drop seperti resumable.js. Berguna untuk mencegah perilaku browser default dalam kasus-kasus di mana pengguna secara tidak sengaja menjatuhkan file yang ingin mereka unggah di luar zona drop-unggah file yang sebenarnya, dan kemudian bertanya-tanya mengapa mereka sekarang melihat file yang sama yang diberikan langsung di jendela browser ( dengan asumsi tipe file yang kompatibel seperti gambar atau video dijatuhkan), daripada perilaku yang diharapkan untuk melihat file mereka diunggah.
bluebinary
15
Catatan: ini juga menonaktifkan menyeret file ke a <input type="file" />. Penting untuk memeriksa apakah e.targetinput file dan membiarkan peristiwa seperti itu lewat.
Sebastian Nowak
6
apa ? mengapa jendela dragover memuat file? ini tidak masuk akal ...
L.Trabacchin
37

Setelah banyak mengutak-atik, saya menemukan ini menjadi solusi paling stabil:

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>

Pengaturan keduanya effectAllowdan dropEffecttanpa syarat pada jendela menyebabkan zona drop saya tidak lagi menerima dnd, terlepas dari apakah properti disetel baru atau tidak.

Axel Amthor
sumber
e.dataTransfer () adalah bagian penting di sini yang membuat pekerjaan ini, yang gagal disebutkan "jawaban diterima".
HoldOffHunger
9

Untuk jQuery jawaban yang benar adalah:

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

Di sini return falseakan berperilaku sebagai event.preventDefault()dan event.stopPropagation().

Penglihatan
sumber
9

Untuk mengizinkan seret-dan-jatuhkan hanya pada beberapa elemen, Anda bisa melakukan sesuatu seperti:

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);
antusias sekali
sumber
Berfungsi sempurna untuk saya, tetapi saya juga akan menambahkan check untuk type = file, jika tidak, Anda masih dapat menarik ke input teks
Andreas Zwerger
2

coba ini:

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

Mencegah semua operasi seret dan lepas secara default mungkin bukan yang Anda inginkan. Mungkin untuk memeriksa apakah sumber seret adalah file eksternal, setidaknya di beberapa browser. Saya telah menyertakan fungsi untuk memeriksa apakah sumber seret adalah file eksternal dalam jawaban StackOverflow ini .

Memodifikasi jawaban Digital Plane, Anda dapat melakukan sesuatu seperti ini:

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
sumber
1
Apa gunanya e || event;? Di mana eventdidefinisikan? Lupakan. Sepertinya itu adalah objek global di IE? Saya menemukan kutipan ini, di "In Microsoft Visual Basic Scripting Edition (VBScript), you must access the event object through the window object." sini
1,21 gigawatt
2

Catatan: Meskipun OP tidak meminta solusi Angular, saya datang ke sini mencari itu. Jadi ini untuk membagikan apa yang saya temukan sebagai solusi yang layak, jika Anda menggunakan Angular.

Dalam pengalaman saya, masalah ini pertama kali muncul ketika Anda menambahkan fungsionalitas penurunan file ke halaman. Karena itu pendapat saya adalah komponen yang menambahkan ini, juga harus bertanggung jawab untuk mencegah drop di luar drop zone.

Dalam solusi saya zona penurunan adalah input dengan kelas, tetapi pemilih yang tidak ambigu berfungsi.

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

Pendengar ditambahkan / dihapus secara otomatis ketika komponen dibuat / dihancurkan, dan komponen lainnya yang menggunakan strategi yang sama pada halaman yang sama tidak saling mengganggu karena StopPropagation ().

Superole
sumber
Ini bekerja seperti pesona !! Browser bahkan mengubah kursor mouse dengan menambahkan ikon larangan yang sangat hebat !!
pti_jul
1

Untuk membangun metode "periksa target" yang dijabarkan dalam beberapa jawaban lain, berikut adalah metode yang lebih umum / fungsional:

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

Disebut seperti:

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
sumber
Juga, bisa menambahkan beberapa ES6 di sini: function preventDefaultExcept(...predicates){}. Dan kemudian gunakan sepertipreventDefaultExcept(isDropzone, isntParagraph)
hlfrmn
0

Saya memiliki HTML object( embed) yang mengisi lebar dan tinggi halaman. Jawaban oleh @ digital-plane berfungsi pada halaman web normal tetapi tidak jika pengguna menjatuhkan ke objek tertanam. Jadi saya butuh solusi berbeda.

Jika kita beralih ke menggunakan fase penangkapan acara, kita bisa mendapatkan peristiwa sebelum objek tertanam menerimanya (perhatikan truenilai di akhir panggilan pendengar acara):

// 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);

Dengan menggunakan kode berikut (berdasarkan jawaban @ digital-plane), halaman menjadi target seret, mencegah embed objek menangkap peristiwa dan kemudian memuat gambar kami:

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);

Diuji pada Firefox pada Mac.

1,21 gigawatt
sumber
0

Saya menggunakan pemilih kelas untuk beberapa area unggahan sehingga solusi saya mengambil bentuk yang kurang murni ini

Berdasarkan jawaban Axel Amthor, dengan ketergantungan pada jQuery (alias $)

_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
sumber