arrastre los archivos de la gota a la entrada estándar del archivo html

En estos días podemos arrastrar y soltar archivos en un contenedor especial y subirlos con XHR 2. Muchos a la vez. Con barras de progreso en vivo, etc. Cosas muy interesantes. Ejemplo aquí.

Pero a veces no queremos tanta frialdad. Lo que me gustaría es arrastrar y soltar archivos, muchos a la vez, en una entrada de archivo HTML estándar : .

¿Es eso posible? ¿Hay alguna forma de ‘completar’ la entrada del archivo con los nombres de archivo correctos (?) Del archivo desplegado? (Los filepaths completos no están disponibles por razones de seguridad del sistema de archivos).

¿Por qué? Porque me gustaría enviar un formulario normal. Para todos los navegadores y todos los dispositivos. La función de arrastrar y soltar es solo una mejora progresiva para mejorar y simplificar UX. El formulario estándar con entrada de archivo estándar (+ atributo multiple ) estará allí. Me gustaría agregar la mejora HTML5.

editar
Sé que en algunos navegadores a veces puedes (casi siempre) colocar archivos en la entrada del archivo. Sé que Chrome generalmente hace esto, pero a veces falla y luego carga el archivo en la página actual (un gran error si estás completando un formulario). Quiero engañar- y a prueba de navegador.

Hice una solución para esto.

 $(function () { var dropZoneId = "drop-zone"; var buttonId = "clickHere"; var mouseOverClass = "mouse-over"; var dropZone = $("#" + dropZoneId); 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"); document.getElementById(dropZoneId).addEventListener("dragover", function (e) { e.preventDefault(); e.stopPropagation(); dropZone.addClass(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); if (buttonId != "") { var clickZone = $("#" + buttonId); var oleft = clickZone.offset().left; var oright = clickZone.outerWidth() + oleft; var otop = clickZone.offset().top; var obottom = clickZone.outerHeight() + otop; $("#" + buttonId).mousemove(function (e) { var x = e.pageX; var y = e.pageY; if (!(x < oleft || x > oright || y < otop || y > obottom)) { inputFile.offset({ top: y - 15, left: x - 160 }); } else { inputFile.offset({ top: -400, left: -400 }); } }); } document.getElementById(dropZoneId).addEventListener("drop", function (e) { $("#" + dropZoneId).removeClass(mouseOverClass); }, true); }) 
 #drop-zone { /*Sort of important*/ width: 300px; /*Sort of important*/ height: 200px; position:absolute; left:50%; top:100px; margin-left:-150px; border: 2px dashed rgba(0,0,0,.3); border-radius: 20px; font-family: Arial; text-align: center; position: relative; line-height: 180px; font-size: 20px; color: rgba(0,0,0,.3); } #drop-zone input { /*Important*/ position: absolute; /*Important*/ cursor: pointer; left: 0px; top: 0px; /*Important This is only comment out for demonstration purposes. opacity:0; */ } /*Important*/ #drop-zone.mouse-over { border: 2px dashed rgba(0,0,0,.5); color: rgba(0,0,0,.5); } /*If you dont want the button*/ #clickHere { position: absolute; cursor: pointer; left: 50%; top: 50%; margin-left: -50px; margin-top: 20px; line-height: 26px; color: white; font-size: 12px; width: 100px; height: 26px; border-radius: 4px; background-color: #3b85c3; } #clickHere:hover { background-color: #4499DD; } 
  
Drop files here...
or click here..

Lo siguiente funciona en Chrome y FF, pero todavía no he encontrado una solución que cubra IE10 +:

 // 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; evt.preventDefault(); }; 
 < !DOCTYPE html>   
Drop Here
Should update here:

Esta es la forma HTML5 de “DTHML” para hacerlo. Entrada de forma normal (que se lee solo como señaló Ricardo Tomasi). Luego, si se arrastra un archivo, se adjunta al formulario. Esto REQUERIRÁ la modificación de la página de acción para aceptar el archivo cargado de esta manera.

 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; } 
 
 //----------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:180px; border: 10px dashed #ccc; } #holder.hover { width:400px; height:180px; border: 10px dashed #0c0 !important; } .hidden { visibility: hidden; } .visible { visibility: visible; } 
 < !DOCTYPE html>    HTML 5     

En teoría, podría agregar un elemento que superponga a , y luego usar su evento drop para capturar los archivos (usando File API) y pasarlos a la matriz de files entrada.

Excepto que una entrada de archivo es de solo lectura . Este es un viejo problema.

Sin embargo, puede omitir el control de formulario por completo y cargarlo a través de XHR (no estoy seguro del soporte para eso):

También puede usar un elemento en el área circundante para cancelar el evento de soltar en Chrome y evitar el comportamiento predeterminado de cargar el archivo.

Dejar caer varios archivos sobre la entrada ya funciona en Safari y Firefox.

Sé que algunos trucos funcionan en Chrome.

Cuando coloca un archivo en la zona de colocación, obtiene un objeto dataTransfer.files, que es un tipo de objeto “FileList” que contiene todos los archivos que arrastró. Mientras tanto, el elemento tiene “archivos” de propiedades, que es el mismo objeto tipo “FileList”.

Por lo tanto, puede simplemente asignar el objeto dataTransfer.files a la propiedad input.files.

Impresionante trabajo de @BjarkeCK. Hice algunas modificaciones a su trabajo, para usarlo como método en 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(); 

Fiddle de trabajo

Para una solución de solo CSS:

 
Click to select a file, or drag it here Great, your file is selected
.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; }

Modificado de https://codepen.io/Scribblerockerz/pen/qdWzJw

Para cualquiera que esté buscando hacer esto en 2018, tengo una solución mucho mejor y más simple que todas las cosas viejas publicadas aquí. Puede hacer un bonito cuadro de arrastrar y soltar con HTML, JavaScript y CSS simples.

(Solo funciona en Chrome hasta el momento)

Comencemos con el HTML.

 

Entonces llegaremos al estilo.

  .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%); } 

Después de haber hecho esto, ya se ve bien. Pero imagino que le gustaría ver qué archivo ha cargado, así que vamos a hacer algo de JavaScript. ¿Recuerdas ese lapso de valor pfp? Ahí es donde vamos a imprimir el nombre del archivo.

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

Y eso es.

Pocos años después, construí esta biblioteca para hacer caer los archivos en cualquier elemento HTML.

Puedes usarlo como

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

Lo que podría hacer es mostrar una entrada de archivo y superponerla con su área de colocación transparente, teniendo cuidado de usar un nombre como file[1] . {Asegúrese de tener el enctype="multipart/form-data" dentro de su etiqueta FORM.}

Luego haga que el área desplegable maneje los archivos adicionales creando dinámicamente más entradas de archivo para los archivos 2..number_of_files, asegúrese de usar el mismo nombre base, llenando el valor-atributo de manera apropiada.

Por último (front-end) envíe el formulario.


Todo lo que se requiere para manejar este método es alterar su procedimiento para manejar una matriz de archivos.