trascina i file di rilascio nell'input di file html standard


163

In questi giorni possiamo trascinare e rilasciare i file in un contenitore speciale e caricarli con XHR 2. Molti alla volta. Con barre di avanzamento dal vivo ecc. Roba molto interessante. Esempio qui.

Ma a volte non vogliamo tanta frescura. Quello che mi piacerebbe è quello di file drag & drop - molti alla volta - in un ingresso di file HTML standard : <input type=file multiple>.

È possibile? Esiste un modo per "riempire" l'input del file con i nomi di file giusti (?) Dalla discesa del file? (I percorsi di file completi non sono disponibili per motivi di sicurezza del file system.)

Perché? Perché vorrei presentare un modulo normale. Per tutti i browser e tutti i dispositivi. Il trascinamento della selezione è solo un miglioramento progressivo per migliorare e semplificare la UX. Il modulo standard con input di file standard (+ multipleattributo) sarà lì. Vorrei aggiungere il miglioramento HTML5.

modifica
So che in alcuni browser a volte è possibile (quasi sempre) rilasciare i file nell'input del file stesso. So che Chrome di solito lo fa, ma a volte non riesce e quindi carica il file nella pagina corrente (un grosso errore se stai compilando un modulo). Voglio ingannarlo e proteggerlo dal browser.


1
Preparati al dolore se vuoi includere mac / safari nelle tue compatibilità.
Shark8,

1
@ Shark8 in realtà Safari / Mac è uno dei pochi browser che già lo supportano.
Ricardo Tomasi,

In realtà, nessuno dei browser supporta questo. Il campo di input del file è di sola lettura (per sicurezza) e questo è il problema. Stupida sicurezza!
Rudie,

2
Con questo intendevo "trascinare e rilasciare i file - molti alla volta - in un input di file HTML standard".
Ricardo Tomasi,

3
trascina / rilascia più file per input type="file" multiplefunzionare correttamente in Safari
Lloyd,

Risposte:


71

Quanto segue funziona in Chrome e FF, ma devo ancora trovare una soluzione che copra anche 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;

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

Probabilmente vorrai usare addEventListenero jQuery (ecc.) Per registrare i tuoi gestori evt - questo è solo per amor di brevità.


3
Waaaaaaaat! Che funzioni!? Questo è esattamente quello che stavo cercando. Non ha funzionato 2 anni fa. Eccezionale! Ovviamente non funziona in IE =) La domanda importante: esiste un rilevamento affidabile delle funzionalità? Quindi puoi nascondere la zona di rilascio in IE, bc non funzionerà.
Rudie,

D'oh, un po 'in ritardo allora :) In questo momento sto solo usando semplici controlli agente utente in JS. Ovviamente devi provare MSIE , Trident/(IE11) e Edge/(IE12) ...
jlb

FF 48.0.2 (Mac) genera "TypeError: impostazione di una proprietà che ha solo un getter" sulla riga fileInput.files = evt.dataTransfer.files;. Safari e Chrome tuttavia funzionano entrambi bene.
Risadinha,

2
Questo esempio non funziona su Firefox 45 su Linux, ma funziona su Chrome. Non visualizzo alcun errore della console, semplicemente non mostra che nessun file è stato eliminato.
Bryan Oakley,

1
in realtà ho scritto un post per cercare di trovare una soluzione ma ho capito da solo. Cambiamento abbastanza semplice, solo fileInputs [index] = ... per passare i dati del file a un determinato input e quindi chiamare una funzione showNext per aggiungere un nuovo input stackoverflow.com/a/43397640/6392779
nick

51

Ho fatto una soluzione per questo.

La funzionalità di trascinamento della selezione per questo metodo funziona solo con Chrome, Firefox e Safari. (Non so se funziona con IE10), ma per altri browser, il pulsante "Oppure fai clic qui" funziona correttamente.

Il campo di input segue semplicemente il mouse quando si trascina un file su un'area e ho aggiunto anche un pulsante.

Opacità del non commento: 0; l'input del file è visibile solo in modo da poter vedere cosa sta succedendo.


Ecco perché ho aggiunto anche un pulsante ^^ Ma hai ragione. Non lo userei eather ... O lo farei!?
BjarkeCK,

Vorrei sapere come dovrebbe funzionare ... sembra che tutte le funzioni di trascinamento della selezione abbiano a che fare con l'aggiunta dell'effetto hover ... ma non riesco davvero a dirlo. Sembra buono nel violino, ma non credo di poterlo usare poiché devo supportare Internet Explorer
nmz787

1
@PiotrKowalski Penso che potrebbe innescare potenzialmente una chiamata ricorsiva fino a quando lo stack di chiamate trabocca
John,

2
Ho finito per usare solo lo stile. Rendere l'ingresso e la larghezza e l'altezza del 100% ha funzionato meglio che spostarlo.
Eddie,

2
C'è un modo per sbarazzarsi del "nessun file scelto" che continua a librarsi insieme al puntatore del mouse? @BjarkeCK
Abhishek Singh,

27

Questo è il modo HTML5 "DTHML" per farlo. Input di forma normale (che IS legge solo come ha sottolineato Ricardo Tomasi). Quindi, se un file viene trascinato, viene allegato al modulo. Ciò richiederà la modifica della pagina dell'azione per accettare il file caricato in questo modo.

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>

È ancora più boss se riesci a rendere l'intera finestra una zona di trascinamento, vedi Come posso rilevare un evento di trascinamento HTML5 che entra e esce dalla finestra, come fa Gmail?


1
Buona soluzione ma non funziona su IE <10 perché IE 9 e meno non supporta l'API di file HTML5 :(
Sviluppo

1
Questa riga: document.getElementById ('fileDragData'). Value = files [i] .slice (); non è necessario, perché è stato sostituito nella funzione
reader.onload

Ecco un'altra simpatica applicazione di trascinamento della selezione che NON prevede il caricamento di file. Collegamento nel caso qualcuno volesse studiare di più. codepen.io/anon/pen/MOPvZK?editors=1010
William Entriken

1
La soluzione di IE 10 è quella di degradare e mostrare soloinput type=file
William Entriken il

Mi sto perdendo qualcosa o sovrascrivi costantemente la .valueproprietà con il file più recente, ogni volta che esegui l'iterazione nel ciclo anteriore?
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>


2
Cosa mostra l'utente? Puoi fare un violino o un esempio online?
Rudie,

@Rudie, fai clic su Esegui snippet di codice e trascina un'immagine per visualizzarla, mostrerà l'anteprima dell'immagine rilasciata.
Dipak,

6

In teoria, è possibile aggiungere un elemento sovrapposto a <input/>, quindi utilizzare l' dropevento per acquisire i file (utilizzando l'API File) e passarli filesall'array di input .

Tranne che un input di file è di sola lettura . Questo è un vecchio problema

Tuttavia, puoi ignorare completamente il controllo del modulo e caricarlo tramite XHR (non sei sicuro del supporto per questo):

È inoltre possibile utilizzare un elemento nell'area circostante per annullare l'evento di rilascio in Chrome e impedire il comportamento predefinito del caricamento del file.

Il rilascio di più file sull'input funziona già in Safari e Firefox.


6
Come ho detto nella domanda: conosco XHR2 e non voglio usarlo. Immagino la parte importante: "l'input del file è di sola lettura". Che schifo ... Annullare l'evento drop non è una cattiva idea! Non buono come speravo, ma probabilmente il migliore. La caduta di più file funziona anche in Chrome a proposito. Chrome ora consente anche il caricamento di directory. Tutto molto kewl e non aiutando il mio caso = (
Rudie,

5

Questo è ciò con cui sono uscito.

Utilizzando Jquery e HTML. Questo lo aggiungerà ai file di inserimento.

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>


4

Per una soluzione solo CSS:

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

Modificato da https://codepen.io/Scribblerockerz/pen/qdWzJw


4

So che alcuni trucchi funzionano in Chrome:

Quando si rilasciano i file nell'area di rilascio si ottiene un dataTransfer.filesoggetto, ovvero un FileListtipo di oggetto, che contiene tutti i file trascinati. Nel frattempo,<input type="file" /> element ha la proprietà files, ovvero lo stesso FileListoggetto type.

Quindi, puoi semplicemente assegnare l' dataTransfer.filesoggetto alla input.filesproprietà.


3
Sì, lo fa in questi giorni. Non è un trucco. Molto intenzionale. Anche molto intenzionalmente molto limitato. Non è possibile aggiungere file all'elenco o modificare completamente l'elenco. Il trascinamento della selezione può ricordare i file e aggiungerli, ma input.filesnon può = (
Rudie

3

Per chiunque stia cercando di farlo nel 2018, ho una soluzione molto migliore e più semplice di tutte le vecchie cose pubblicate qui. Puoi creare una casella drag & drop dall'aspetto piacevole con solo HTML, JavaScript e CSS vanilla.

(Funziona solo con Chrome finora)

Cominciamo con l'HTML.

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

Quindi arriveremo allo styling.

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

Dopo averlo fatto, sembra già a posto. Ma immagino che ti piacerebbe vedere quale file hai effettivamente caricato, quindi faremo un po 'di JavaScript. Ricordi quell'intervallo di valori pfp? Ecco dove stamperemo il nome del 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;
    }
});

E questo è tutto.


Ottengo un TypeError Uncaught: non riesco a leggere la proprietà 'addEventListener' di null quando utilizzo questo codice - in Chrome - non funziona nelle ultime versioni di Chrome?
Combatti il ​​fuoco con il fuoco

Funziona bene per me nell'ultima versione di Chrome. Assicurati di utilizzare gli ID corretti
Michael

1

Fantastico lavoro di @BjarkeCK. Ho apportato alcune modifiche al suo lavoro, per usarlo come metodo in 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 di lavoro


1
Cordiali saluti: Fiddle link è rotto.
jimiayler,

1

Pochi anni dopo, ho costruito questa biblioteca per rilasciare file in qualsiasi elemento HTML.

Puoi usarlo come

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

come si recupera il file selezionato in un secondo momento quando si invia il modulo?
Nikhil VJ,

-1

Quello che potresti fare è visualizzare un input di file e sovrapporlo con la tua area di rilascio trasparente, facendo attenzione a usare un nome come file[1]. {Assicurati di avere ilenctype="multipart/form-data" interno del tuo tag FORM.}

Quindi fai in modo che l'area di rilascio gestisca i file extra creando dinamicamente più input di file per i file 2..number_of_files, assicurati di utilizzare lo stesso nome di base, popolando l'attributo value in modo appropriato.

Infine (front-end) inviare il modulo.


Tutto ciò che è necessario per gestire questo metodo è modificare la procedura per gestire una matrice di file.


1
In multiplequesti giorni l' input del file ha un attributo. Non è necessario inserire più di 1 file. Questo non è il problema però. Come ottengo gli Fileoggetti nell'input del file? Penso che questo richieda un esempio di codice ...
Rudie,

1
@Rudie non puoi, questo è il problema.
Ricardo Tomasi,

1
Non puoi cosa? Multipla? Si, puoi. L'ho appena detto. Il multiplo non è il problema. Il problema è ottenere i file da un oggetto File (trascinato) in un input di file.
Rudie,

@Rudie ottenere file trascinati in un input di file è possibile con Chrome / FF (usando la filesproprietà), ma non sono riuscito in IE - hai avuto fortuna?
jlb,

1
@jlb Cosa intendi con "utilizzo della proprietà files"? Potresti rispondere con un codice pertinente? Quello che stavo cercando non funziona / non esiste in nessun browser.
Rudie,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.