seret jatuhkan file ke input file html standar

163

Saat ini kami dapat menarik & melepas file ke wadah khusus dan mengunggahnya dengan XHR 2. Banyak sekaligus. Dengan bilah kemajuan langsung, dll. Hal-hal yang sangat keren. Contoh di sini.

Tetapi kadang-kadang kita tidak menginginkan kesejukan sebanyak itu. Apa yang saya ingin adalah untuk drag & drop file - banyak pada satu waktu - menjadi masukan file HTML standar : <input type=file multiple>.

Apakah itu mungkin? Apakah ada cara untuk 'mengisi' input file dengan nama file yang benar (?) Dari drop file? (Berkas lengkap tidak tersedia karena alasan keamanan sistem file.)

Mengapa? Karena saya ingin mengirimkan formulir normal. Untuk semua browser dan semua perangkat. Seret & lepas hanyalah penyempurnaan progresif untuk menyempurnakan & menyederhanakan UX. Formulir standar dengan input file standar (+ multipleatribut) akan ada di sana. Saya ingin menambahkan peningkatan HTML5.

sunting
Saya tahu di beberapa browser Anda kadang - kadang dapat (hampir selalu) memasukkan file ke dalam input file itu sendiri. Saya tahu Chrome biasanya melakukan ini, tetapi kadang-kadang gagal dan kemudian memuat file di halaman saat ini (gagal besar jika Anda mengisi formulir). Saya ingin menipu & tahan browser.

Rudie
sumber
1
Bersiaplah untuk rasa sakit jika Anda ingin memasukkan mac / safari dalam kompatibilitas Anda.
Shark8
1
@ Shark8 sebenarnya Safari / Mac adalah salah satu dari beberapa browser yang sudah mendukung ini.
Ricardo Tomasi
Sebenarnya, tidak ada browser yang mendukung ini. Kolom input file hanya-baca (untuk keamanan) dan itulah masalahnya. Keamanan bodoh!
Rudie
2
Dengan ini saya berarti "drag & drop file - banyak pada satu waktu - menjadi masukan file HTML standar".
Ricardo Tomasi
3
seret / jatuhkan beberapa file agar input type="file" multipleberfungsi dengan baik di Safari
Lloyd

Jawaban:

71

Berikut ini berfungsi di Chrome dan FF, tetapi saya belum menemukan solusi yang mencakup IE10 + juga:

// dragover and dragenter events need to have 'preventDefault' called
// in order for the 'drop' event to register. 
// See: https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Drag_operations#droptargets
dropContainer.ondragover = dropContainer.ondragenter = function(evt) {
  evt.preventDefault();
};

dropContainer.ondrop = function(evt) {
  // pretty simple -- but not for IE :(
  fileInput.files = evt.dataTransfer.files;

  // If you want to use some of the dropped files
  const dT = new DataTransfer();
  dT.items.add(evt.dataTransfer.files[0]);
  dT.items.add(evt.dataTransfer.files[3]);
  fileInput.files = dT.files;

  evt.preventDefault();
};
<!DOCTYPE html>
<html>
<body>
<div id="dropContainer" style="border:1px solid black;height:100px;">
   Drop Here
</div>
  Should update here:
  <input type="file" id="fileInput" />
</body>
</html>

Anda mungkin ingin menggunakan addEventListeneratau jQuery (dll.) Untuk mendaftarkan penangan evt Anda - ini hanya demi singkatnya.

jlb
sumber
3
Waaaaaaaaat! Itu bekerja!? Itulah tepatnya yang saya cari. Tidak berfungsi 2 tahun yang lalu. Luar biasa! Tentu saja itu tidak berfungsi di IE =) Pertanyaan penting: apakah ada deteksi fitur yang dapat diandalkan ?, sehingga Anda dapat menyembunyikan dropzone di IE, karena itu tidak akan berfungsi.
Rudie
Doh, agak terlambat kalau begitu :) Saat ini saya hanya menggunakan cek agen pengguna sederhana di JS. Tentu saja Anda harus menguji MSIE , Trident/(IE11) dan Edge/(IE12) ...
jlb
FF 48.0.2 (Mac) melempar "TypeError: mengatur properti yang hanya memiliki pengambil" pada baris fileInput.files = evt.dataTransfer.files;. Safari dan Chrome keduanya berfungsi dengan baik.
Risadinha
2
Contoh ini tidak bekerja di firefox 45 di linux, tetapi ini berfungsi untuk saya di chrome. Saya tidak mendapatkan kesalahan konsol, itu hanya tidak menunjukkan bahwa file apa pun dijatuhkan.
Bryan Oakley
1
sebenarnya saya membuat posting untuk mencoba dan menemukan solusi tetapi menemukan sendiri. Perubahan yang cukup sederhana, cukup fileInputs [index] = ... untuk meneruskan data file ke input tertentu dan kemudian memanggil fungsi showNext untuk menambahkan input stackoverflow.com/a/43397640/6392779 baru
nick
51

Saya membuat solusi untuk ini.

Fungsionalitas Drag and Drop untuk metode ini hanya berfungsi dengan Chrome, Firefox dan Safari (Tidak tahu apakah itu berfungsi dengan IE10), tetapi untuk browser lain, tombol "Atau klik di sini" berfungsi dengan baik.

Kolom input cukup ikuti mouse Anda ketika menyeret file ke suatu area, dan saya telah menambahkan tombol juga ..

Opacity komentar: 0; input file hanya terlihat sehingga Anda dapat melihat apa yang terjadi.

BjarkeCK
sumber
Itu sebabnya saya menambahkan tombol juga ^^ Tapi yah Anda benar. Saya tidak akan menggunakannya Eather ... Atau saya akan !?
BjarkeCK
Saya berharap saya tahu bagaimana itu seharusnya bekerja ... sepertinya semua fungsi drag / drop harus berurusan dengan menambahkan efek hover ... tapi saya benar-benar tidak tahu. Terlihat bagus di biola, tapi saya rasa saya tidak bisa menggunakannya karena saya perlu mendukung Internet Explorer
nmz787
1
@PiotrKowalski Saya pikir itu berpotensi memicu panggilan rekursif sampai tumpukan panggilan meluap
John
2
Saya akhirnya menggunakan gaya saja. Membuat input 100% lebar dan tinggi bekerja lebih baik daripada memindahkannya.
Eddie
2
Apakah ada cara untuk menyingkirkan "tidak ada file yang dipilih" yang terus melayang bersama dengan pointer mouse kita? @BjarkeCK
Abhishek Singh
27

Ini adalah cara "DTHML" HTML5 untuk melakukannya. Input bentuk normal (yang IS hanya dibaca seperti yang ditunjukkan Ricardo Tomasi). Kemudian jika file diseret, itu dilampirkan ke formulir. Ini AKAN memerlukan modifikasi pada halaman tindakan untuk menerima file yang diunggah dengan cara ini.

function readfiles(files) {
  for (var i = 0; i < files.length; i++) {
    document.getElementById('fileDragName').value = files[i].name
    document.getElementById('fileDragSize').value = files[i].size
    document.getElementById('fileDragType').value = files[i].type
    reader = new FileReader();
    reader.onload = function(event) {
      document.getElementById('fileDragData').value = event.target.result;}
    reader.readAsDataURL(files[i]);
  }
}
var holder = document.getElementById('holder');
holder.ondragover = function () { this.className = 'hover'; return false; };
holder.ondragend = function () { this.className = ''; return false; };
holder.ondrop = function (e) {
  this.className = '';
  e.preventDefault();
  readfiles(e.dataTransfer.files);
}
#holder.hover { border: 10px dashed #0c0 !important; }
<form method="post" action="http://example.com/">
  <input type="file"><input id="fileDragName"><input id="fileDragSize"><input id="fileDragType"><input id="fileDragData">
  <div id="holder" style="width:200px; height:200px; border: 10px dashed #ccc"></div>
</form>

Itu bahkan lebih bos jika Anda bisa membuat seluruh jendela zona jatuh, lihat Bagaimana cara mendeteksi acara seret HTML5 memasuki dan meninggalkan jendela, seperti yang dilakukan Gmail?

William Entriken
sumber
1
Solusi yang bagus namun tidak bekerja pada IE <10 karena IE 9 dan kurang tidak mendukung file HTML5 API :(
Develoger
1
Baris ini: document.getElementById ('fileDragData'). Value = file [i] .slice (); tidak diperlukan, karena digantikan oleh fungsi
reader.onload
Berikut ini adalah aplikasi seret dan jatuhkan yang lucu yang TIDAK melibatkan unggahan file. Menghubungkan untuk berjaga-jaga jika seseorang ingin belajar lebih banyak. codepen.io/anon/pen/MOPvZK?editors=1010
William Entriken
1
Solusi IE 10 adalah menurunkan dan hanya menunjukkaninput type=file
William Entriken
Apakah saya kehilangan sesuatu, atau apakah Anda terus-menerus menimpa .valueproperti dengan file terbaru, setiap kali Anda beralih melalui loop kedepan?
Kevin Burke
13

//----------App.js---------------------//
$(document).ready(function() {
    var holder = document.getElementById('holder');
    holder.ondragover = function () { this.className = 'hover'; return false; };
    holder.ondrop = function (e) {
      this.className = 'hidden';
      e.preventDefault();
      var file = e.dataTransfer.files[0];
      var reader = new FileReader();
      reader.onload = function (event) {
          document.getElementById('image_droped').className='visible'
          $('#image_droped').attr('src', event.target.result);
      }
      reader.readAsDataURL(file);
    };
});
.holder_default {
    width:500px; 
    height:150px; 
    border: 3px dashed #ccc;
}

#holder.hover { 
    width:400px; 
    height:150px; 
    border: 3px dashed #0c0 !important; 
}

.hidden {
    visibility: hidden;
}

.visible {
    visibility: visible;
}
<!DOCTYPE html>

<html>
    <head>
        <title> HTML 5 </title>
        <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.js"></script>
    </head>
    <body>
      <form method="post" action="http://example.com/">
        <div id="holder" style="" id="holder" class="holder_default">
          <img src="" id="image_droped" width="200" style="border: 3px dashed #7A97FC;" class=" hidden"/>
        </div>
      </form>
    </body>
</html>

Dipak
sumber
2
Apa yang ditunjukkannya kepada pengguna? Bisakah Anda membuat contoh biola atau online?
Rudie
@Rudie silakan klik run code snippet dan seret-n-jatuhkan satu gambar untuk dilihat, itu akan menampilkan pratinjau gambar yang dijatuhkan.
Dipak
6

Secara teori, Anda bisa menambahkan elemen overlay <input/>, dan kemudian menggunakannya dropuntuk menangkap file (menggunakan File API) dan meneruskannya ke input filesarray.

Kecuali bahwa input file hanya baca . Ini masalah lama.

Namun Anda dapat, melewati kontrol formulir sepenuhnya dan mengunggah melalui XHR (tidak yakin tentang dukungan untuk itu):

Anda juga bisa menggunakan elemen di area sekitarnya untuk membatalkan acara drop di Chrome, dan mencegah perilaku default memuat file.

Menjatuhkan banyak file melalui input sudah berfungsi di Safari dan Firefox.

Ricardo Tomasi
sumber
6
Seperti yang saya katakan dalam pertanyaan: Saya tahu XHR2 dan saya tidak ingin menggunakannya. Saya kira bagian iportant: "input file hanya-baca". Itu menyebalkan ... Membatalkan acara drop bukan ide yang buruk! Tidak sebagus yang kuharapkan, tapi mungkin yang terbaik. Menjatuhkan banyak file berfungsi di Chrome juga btw. Chrome sekarang juga memungkinkan mengunggah direktori. Semua sangat kewl dan tidak membantu kasus saya = (
Rudie
5

Inilah yang saya hasilkan.

Menggunakan Jquery dan Html. Ini akan menambahkannya ke file yang disisipkan.

var dropzone = $('#dropzone')


dropzone.on('drag dragstart dragend dragover dragenter dragleave drop', function(e) {
    e.preventDefault();
    e.stopPropagation();
  })

dropzone.on('dragover dragenter', function() {
    $(this).addClass('is-dragover');
  })
dropzone.on('dragleave dragend drop', function() {
    $(this).removeClass('is-dragover');
  })  
  
dropzone.on('drop',function(e) {
	var files = e.originalEvent.dataTransfer.files;
	// Now select your file upload field 
	// $('input_field_file').prop('files',files)
  });
input {	margin: 15px 10px !important;}

.dropzone {
	padding: 50px;
	border: 2px dashed #060;
}

.dropzone.is-dragover {
  background-color: #e6ecef;
}

.dragover {
	bg-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<div class="" draggable='true' style='padding: 20px'>
	<div id='dropzone' class='dropzone'>
		Drop Your File Here
	</div>
	</div>

Lionel Yeo
sumber
4

Untuk solusi CSS saja:

<div class="file-area">
    <input type="file">
    <div class="file-dummy">
        <span class="default">Click to select a file, or drag it here</span>
        <span class="success">Great, your file is selected</span>
    </div>
</div>

.file-area {
    width: 100%;
    position: relative;
    font-size: 18px;
}
.file-area input[type=file] {
    position: absolute;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    opacity: 0;
    cursor: pointer;
}
.file-area .file-dummy {
    width: 100%;
    padding: 50px 30px;
    border: 2px dashed #ccc;
    background-color: #fff;
    text-align: center;
    transition: background 0.3s ease-in-out;
}
.file-area .file-dummy .success {
    display: none;
}
.file-area:hover .file-dummy {
    border: 2px dashed #1abc9c;
}
.file-area input[type=file]:valid + .file-dummy {
    border-color: #1abc9c;
}
.file-area input[type=file]:valid + .file-dummy .success {
    display: inline-block;
}
.file-area input[type=file]:valid + .file-dummy .default {
    display: none;
}

Dimodifikasi dari https://codepen.io/Scribblerockerz/pen/qdWzJw

Jonathan
sumber
4

Saya tahu beberapa trik berhasil di Chrome:

Ketika menjatuhkan file ke zona drop Anda mendapatkan dataTransfer.filesobjek, yaitu FileListjenis objek, yang berisi semua file yang Anda seret. Sementara itu, <input type="file" />elemen memiliki properti files, yaitu FileListobjek bertipe sama .

Jadi, Anda cukup menetapkan dataTransfer.filesobjek ke input.filesproperti.

Timur Gilauri
sumber
3
Ya, itu hari ini. Bukan tipuan. Sangat disengaja. Juga sangat sengaja sangat dibatasi. Anda tidak dapat menambahkan file ke daftar, atau mengubah daftar sama sekali. Menyeret dan menjatuhkan dapat mengingat file, dan menambahkannya, tetapi input.filestidak bisa = (
Rudie
3

Bagi siapa saja yang ingin melakukan ini pada 2018, saya punya solusi yang jauh lebih baik dan lebih sederhana daripada semua hal lama yang diposting di sini. Anda dapat membuat kotak seret & jatuhkan yang terlihat bagus hanya dengan vanilla HTML, JavaScript, dan CSS.

(Hanya bekerja di Chrome sejauh ini)

Mari kita mulai dengan HTML.

<div>
<input type="file" name="file" id="file" class="file">
<span id="value"></span>
</div>

Lalu kita akan sampai ke penataan.

    .file {
        width: 400px;
        height: 50px;
        background: #171717;
        padding: 4px;
        border: 1px dashed #333;
        position: relative;
        cursor: pointer;
    }

    .file::before {
        content: '';
        position: absolute;
        background: #171717;
        font-size: 20px;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        width: 100%;
        height: 100%;
    }

    .file::after {
        content: 'Drag & Drop';
        position: absolute;
        color: #808080;
        font-size: 20px;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
    }

Setelah Anda melakukan ini sudah terlihat baik-baik saja. Tapi saya membayangkan Anda ingin melihat file apa yang Anda unggah secara penuh, jadi kita akan melakukan JavaScript. Ingat rentang nilai pfp itu? Di situlah kami akan mencetak nama file.

let file = document.getElementById('file');
file.addEventListener('change', function() {
    if(file && file.value) {
        let val = file.files[0].name;
        document.getElementById('value').innerHTML = "Selected" + val;
    }
});

Dan itu saja.

Michael
sumber
Saya mendapatkan UnEught TypeError: Tidak dapat membaca properti 'addEventListener' dari nol ketika saya menggunakan kode ini - di bawah Chrome - apakah itu tidak berfungsi di versi terbaru Chrome?
Fight Fire With Fire
Ini berfungsi dengan baik untuk saya di Chrome versi terbaru. Pastikan Anda menggunakan ID yang benar
Michael
1

Kerja luar biasa oleh @BjarkeCK. Saya membuat beberapa modifikasi pada karyanya, untuk menggunakannya sebagai metode di jquery:

$.fn.dropZone = function() {
  var buttonId = "clickHere";
  var mouseOverClass = "mouse-over";

  var dropZone = this[0];
  var $dropZone = $(dropZone);
  var ooleft = $dropZone.offset().left;
  var ooright = $dropZone.outerWidth() + ooleft;
  var ootop = $dropZone.offset().top;
  var oobottom = $dropZone.outerHeight() + ootop;
  var inputFile = $dropZone.find("input[type='file']");
  dropZone.addEventListener("dragleave", function() {
    this.classList.remove(mouseOverClass);
  });
  dropZone.addEventListener("dragover", function(e) {
    console.dir(e);
    e.preventDefault();
    e.stopPropagation();
    this.classList.add(mouseOverClass);
    var x = e.pageX;
    var y = e.pageY;

    if (!(x < ooleft || x > ooright || y < ootop || y > oobottom)) {
      inputFile.offset({
        top: y - 15,
        left: x - 100
      });
    } else {
      inputFile.offset({
        top: -400,
        left: -400
      });
    }

  }, true);
  dropZone.addEventListener("drop", function(e) {
    this.classList.remove(mouseOverClass);
  }, true);
}

$('#drop-zone').dropZone();

Biola yang Bekerja

Tuan_Green
sumber
1
FYI: Tautan biola terputus.
jimiayler
1

Beberapa tahun kemudian, saya telah membangun perpustakaan ini untuk melakukan drop file ke elemen HTML apa pun.

Anda bisa menggunakannya seperti

const Droppable = require('droppable');

const droppable = new Droppable({
    element: document.querySelector('#my-droppable-element')
})

droppable.onFilesDropped((files) => {
    console.log('Files were dropped:', files);
});

// Clean up when you're done!
droppable.destroy();
Joel Hernandez
sumber
bagaimana cara mengambil file yang dipilih nanti ketika mengirimkan formulir?
Nikhil VJ
-1

Apa yang bisa Anda lakukan, adalah menampilkan input file dan menaruhnya dengan drop-area transparan Anda, berhati-hati untuk menggunakan nama seperti file[1]. {Pastikan untuk memiliki bagian enctype="multipart/form-data"dalam tag FORMULIR Anda.}

Kemudian minta drop-area menangani file tambahan dengan secara dinamis membuat lebih banyak input file untuk file 2..number_of_files, pastikan untuk menggunakan nama dasar yang sama, isi atribut nilai dengan tepat.

Terakhir (front-end) kirimkan formulir.


Semua yang diperlukan untuk menangani metode ini adalah mengubah prosedur Anda untuk menangani berbagai file.

Shark8
sumber
1
Input file memiliki multipleatribut hari ini. Tidak perlu lebih dari 1 input file. Tapi bukan itu masalahnya. Bagaimana cara memasukkan Fileobjek ke dalam input file? Saya pikir ini memerlukan beberapa contoh kode ...
Rudie 11/11
1
@Rudie Anda tidak bisa, itu masalahnya.
Ricardo Tomasi
1
Tidak bisa apa? Banyak? Ya kamu bisa. Saya baru saja mengatakan itu. Kelipatannya bukan masalah. Semakin mendapatkan file dari objek file yang diseret ke input file, itulah masalahnya.
Rudie
@Rudie mendapatkan file yang diseret ke dalam input file dimungkinkan dengan Chrome / FF (menggunakan filesproperti), tetapi saya belum berhasil di IE - apakah Anda beruntung?
jlb
1
@ jlb Apa maksudmu "menggunakan properti file"? Bisakah Anda membuat jawaban dengan kode yang relevan? Apa yang saya cari tidak berfungsi / ada di browser apa pun.
Rudie