Ziehen Sie Drop-Dateien in die Standard-HTML-Dateieingabe

163

Heutzutage können wir Dateien per Drag & Drop in einen speziellen Container ziehen und mit XHR 2 hochladen. Viele gleichzeitig. Mit Live-Fortschrittsbalken usw. Sehr cooles Zeug. Beispiel hier.

Aber manchmal wollen wir nicht so viel Coolness. Ich möchte Dateien - viele gleichzeitig - per Drag & Drop in eine Standard-HTML-Dateieingabe ziehen : <input type=file multiple>.

Ist das möglich? Gibt es eine Möglichkeit, die Dateieingabe mit den richtigen Dateinamen (?) Aus der Dateiablage zu füllen? (Aus Gründen der Dateisystemsicherheit sind keine vollständigen Dateipfade verfügbar.)

Warum? Weil ich ein normales Formular einreichen möchte. Für alle Browser und alle Geräte. Das Drag & Drop ist nur eine progressive Verbesserung, um UX zu verbessern und zu vereinfachen. Das Standardformular mit Standarddateieingabe (+ multipleAttribut) wird dort sein. Ich möchte die HTML5-Erweiterung hinzufügen.

bearbeiten
weiß , dass ich in einigen Browsern , kann man manchmal (fast immer) selbst Dateien in das Dateieingabe fallen. Ich weiß, dass Chrome dies normalerweise tut, aber manchmal schlägt es fehl und lädt dann die Datei auf der aktuellen Seite (ein großer Fehler, wenn Sie ein Formular ausfüllen). Ich möchte es narrensicher machen.

Rudie
quelle
1
Bereiten Sie sich auf Schmerzen vor, wenn Sie Mac / Safari in Ihre Kompatibilität einbeziehen möchten.
Shark8
1
@ Shark8 Safari / Mac ist einer der wenigen Browser, die dies bereits unterstützen.
Ricardo Tomasi
Tatsächlich unterstützt keiner der Browser dies. Das Dateieingabefeld ist aus Sicherheitsgründen schreibgeschützt und das ist das Problem. Dumme Sicherheit!
Rudie
2
Durch diese bedeutete i „Drag & Drop Dateien - viele zu einer Zeit - in eine Standard - HTML - Datei Eingang“.
Ricardo Tomasi
3
Ziehen Sie mehrere Dateien per Drag & Drop, um input type="file" multiplein Safari
Lloyd

Antworten:

71

Folgendes funktioniert in Chrome und FF, aber ich habe noch keine Lösung gefunden, die auch IE10 + abdeckt:

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

Sie möchten wahrscheinlich addEventListeneroder jQuery (usw.) verwenden, um Ihre evt-Handler zu registrieren - dies ist nur der Kürze halber.

jlb
quelle
3
Waaaaaaaat! Das funktioniert!? Das ist genau das, was ich suchte. Hat vor 2 Jahren nicht funktioniert. Genial! Natürlich funktioniert es nicht im IE =) Die wichtige Frage: Gibt es eine zuverlässige Feature-Erkennung? Sie können also die Dropzone im IE ausblenden, da dies nicht funktioniert.
Rudie
D'oh, dann ein bisschen spät :) Im Moment benutze ich nur einfache User Agent Checks in JS. Natürlich haben Sie zu Test für MSIE , Trident/(IE11) und Edge/(IE12) ...
JLB
FF 48.0.2 (Mac) löst "TypeError: Festlegen einer Eigenschaft, die nur einen Getter enthält" in der Zeile aus fileInput.files = evt.dataTransfer.files;. Safari und Chrome funktionieren jedoch beide einwandfrei.
Risadinha
2
Dieses Beispiel funktioniert nicht unter Firefox 45 unter Linux, aber es funktioniert für mich unter Chrome. Ich erhalte keine Konsolenfehler, es wird einfach nicht angezeigt, dass eine Datei gelöscht wurde.
Bryan Oakley
1
Eigentlich habe ich einen Beitrag geschrieben, um eine Lösung zu finden, aber ich habe es selbst herausgefunden. Ziemlich einfache Änderung, nur fileInputs [index] = ..., um die Dateidaten an eine bestimmte Eingabe zu übergeben und dann eine Funktion showNext aufzurufen, um eine neue Eingabe hinzuzufügen. Stackoverflow.com/a/43397640/6392779
nick
51

Ich habe eine Lösung dafür gefunden.

Die Drag & Drop-Funktion für diese Methode funktioniert nur mit Chrome, Firefox und Safari. (Weiß nicht, ob es mit IE10 funktioniert), aber für andere Browser funktioniert die Schaltfläche "Oder hier klicken" einwandfrei.

Das Eingabefeld folgt einfach Ihrer Maus, wenn Sie eine Datei über einen Bereich ziehen, und ich habe auch eine Schaltfläche hinzugefügt.

Kommentarkommentar: 0; Die Dateieingabe ist nur sichtbar, sodass Sie sehen können, was los ist.

BjarkeCK
quelle
Deshalb habe ich auch einen Knopf hinzugefügt ^^ Aber du hast recht. Ich würde es nicht other benutzen ... Oder würde ich !?
BjarkeCK
Ich wünschte, ich wüsste, wie das funktionieren soll ... anscheinend müssen sich alle Drag & Drop-Funktionen mit dem Hinzufügen des Hover-Effekts befassen ... aber ich kann es wirklich nicht sagen. Sieht in der Geige gut aus, aber ich glaube nicht, dass ich es verwenden kann, da ich Internet Explorer unterstützen muss
nmz787
1
@PiotrKowalski Ich denke, das würde möglicherweise einen rekursiven Aufruf auslösen, bis der Aufrufstapel überläuft
John
2
Am Ende habe ich nur den Stil verwendet. Die Eingabe von 100% Breite und Höhe der Eingabe funktionierte besser als das Verschieben.
Eddie
2
Gibt es eine Möglichkeit, die "keine Datei ausgewählt" zu entfernen, die zusammen mit unserem Mauszeiger schwebt? @BjarkeCK
Abhishek Singh
27

Dies ist die HTML5-Methode "DTHML". Eingabe in normaler Form (die nur gelesen werden kann, wie Ricardo Tomasi hervorhob). Wenn eine Datei eingezogen wird, wird sie an das Formular angehängt. Dies erfordert eine Änderung der Aktionsseite, um die auf diese Weise hochgeladene Datei zu akzeptieren.

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>

Es ist noch besser, wenn Sie das gesamte Fenster zu einer Drop-Zone machen können. Siehe Wie erkenne ich ein HTML5-Drag-Ereignis, das wie Google Mail in das Fenster eintritt und es verlässt?

William Entriken
quelle
1
Gute Lösung, aber es funktioniert nicht auf IE <10, da IE 9 und weniger keine HTML5-Dateien unterstützt. API :(
Develoger
1
Diese Zeile: document.getElementById ('fileDragData'). Value = files [i] .slice (); wird nicht benötigt, da es in der reader.onload-Funktion ersetzt wird
kurdtpage
Hier ist eine weitere nette Drag & Drop-Anwendung, die KEINE Datei-Uploads beinhaltet. Verknüpfen für den Fall, dass jemand mehr lernen möchte. codepen.io/anon/pen/MOPvZK?editors=1010
William Entriken
1
Die IE 10-Lösung besteht darin, dieinput type=file
William Entriken
Fehlt mir etwas oder überschreiben Sie die .valueEigenschaft jedes Mal, wenn Sie die Vorschleife durchlaufen, ständig mit der neuesten Datei?
Kevin Burke
12

//----------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
quelle
2
Was zeigt es dem Benutzer? Können Sie ein Geigen- oder Online-Beispiel machen?
Rudie
@Rudie Bitte klicken Sie auf Code-Snippet ausführen und ziehen Sie ein Bild per Drag & Drop, um es anzuzeigen. Es zeigt die Vorschau des abgelegten Bildes.
Dipak
6

Theoretisch könnten Sie ein Element hinzufügen, das das überlagert <input/>, und dann das dropEreignis verwenden, um die Dateien (mithilfe der Datei-API) zu erfassen und an die Eingabe zu übergebenfiles .

Nur dass eine Dateieingabe schreibgeschützt ist . Das ist ein altes Problem.

Sie können das Formularsteuerelement jedoch vollständig umgehen und über XHR hochladen (nicht sicher, ob dies unterstützt wird):

Sie können auch ein Element in der Umgebung verwenden, um das Löschereignis in Chrome abzubrechen und das Standardverhalten beim Laden der Datei zu verhindern.

Das Löschen mehrerer Dateien über der Eingabe funktioniert bereits in Safari und Firefox.

Ricardo Tomasi
quelle
6
Wie ich in der Frage sagte: Ich kenne XHR2 und möchte es nicht verwenden. Ich denke der wichtige Teil: "Die Dateieingabe ist schreibgeschützt". Das ist scheiße ... Das Drop-Event abzubrechen ist keine schlechte Idee! Nicht so gut wie ich gehofft hatte, aber wahrscheinlich das Beste. Das Löschen mehrerer Dateien funktioniert übrigens auch in Chrome. Chrome ermöglicht jetzt auch das Hochladen von Verzeichnissen. Alles sehr kewl und nicht hilfreich für meinen Fall = (
Rudie
5

Damit bin ich herausgekommen.

Verwenden von Jquery und HTML. Dadurch wird es zu den Einfügedateien hinzugefügt.

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
quelle
4

Für eine reine CSS-Lösung:

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

Geändert von https://codepen.io/Scribblerockerz/pen/qdWzJw

Jonathan
quelle
4

Ich weiß, dass einige Tricks in Chrome funktionieren:

Wenn Sie Dateien in die Ablagezone ablegen, erhalten Sie ein dataTransfer.filesObjekt, dh einen Objekttyp, der FileListalle von Ihnen gezogenen Dateien enthält. In der Zwischenzeit <input type="file" />hat das Element die Eigenschaft files, dass es sich um das gleiche FileListObjekt handelt.

Sie können das dataTransfer.filesObjekt also einfach der input.filesEigenschaft zuweisen .

Timur Gilauri
quelle
3
Ja, das tut es heutzutage. Kein Trick. Sehr absichtlich. Auch sehr absichtlich sehr eingeschränkt. Sie können der Liste keine Dateien hinzufügen oder die Liste überhaupt nicht ändern. Durch Ziehen und Ablegen können Dateien gespeichert und hinzugefügt werden, jedoch input.filesnicht = (
Rudie,
3

Für alle, die dies 2018 tun möchten, habe ich eine viel bessere und einfachere Lösung als all die alten Sachen, die hier veröffentlicht wurden. Sie können eine gut aussehende Drag & Drop-Box mit nur Vanille-HTML, JavaScript und CSS erstellen.

(Funktioniert bisher nur in Chrome)

Beginnen wir mit dem HTML.

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

Dann kommen wir zum 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%);
    }

Nachdem Sie dies getan haben, sieht es bereits gut aus. Aber ich kann mir vorstellen, dass Sie sehen möchten, welche Datei Sie tatsächlich hochgeladen haben, also werden wir JavaScript erstellen. Erinnern Sie sich an diese pfp-Wertspanne? Dort drucken wir den Dateinamen aus.

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

Und das ist es.

Michael
quelle
Ich erhalte einen nicht erfassten Typfehler: Die Eigenschaft 'addEventListener' von null kann nicht gelesen werden, wenn ich diesen Code verwende - unter Chrome - funktioniert er in den neuesten Versionen von Chrome nicht?
Bekämpfe Feuer mit Feuer
Es funktioniert gut für mich in der neuesten Version von Chrome. Stellen Sie sicher, dass Sie die richtigen IDs verwenden
Michael
1

Tolle Arbeit von @BjarkeCK. Ich habe einige Änderungen an seiner Arbeit vorgenommen, um sie als Methode in jquery zu verwenden:

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

Geige arbeiten

Herr Green
quelle
1
Zu Ihrer Information: Die Geigenverbindung ist unterbrochen.
Jimiayler
1

Einige Jahre später habe ich diese Bibliothek gebaut , um Dateien in ein beliebiges HTML-Element abzulegen.

Sie können es wie verwenden

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
quelle
Wie ruft man die ausgewählte Datei später beim Absenden des Formulars ab?
Nikhil VJ
-1

Sie können eine Dateieingabe anzeigen und mit Ihrem transparenten Ablagebereich überlagern, wobei Sie darauf achten, einen Namen wie zu verwenden file[1]. {Stellen Sie sicher, dass sich das enctype="multipart/form-data"FORM-Tag in Ihrem FORM-Tag befindet.}

Lassen Sie dann den Drop-Bereich die zusätzlichen Dateien verarbeiten, indem Sie dynamisch mehr Dateieingaben für Dateien 2..number_of_files erstellen. Stellen Sie sicher, dass Sie denselben Basisnamen verwenden und das value-Attribut entsprechend ausfüllen.

Zuletzt (Frontend) senden Sie das Formular.


Um diese Methode zu handhaben, müssen Sie lediglich Ihre Prozedur ändern, um ein Array von Dateien zu handhaben.

Shark8
quelle
1
Die Dateieingabe hat multipleheutzutage ein Attribut. Es ist nicht mehr als 1 Dateieingabe erforderlich. Das ist jedoch nicht das Problem. Wie bekomme ich die FileObjekte in die Dateieingabe? Ich denke, dies erfordert ein Codebeispiel ...
Rudie
1
@ Rudie kannst du nicht, das ist das Problem.
Ricardo Tomasi
1
Kannst du nicht was? Mehrere? Ja, du kannst. Ich habe das gerade gesagt. Das Vielfache ist nicht das Problem. Das Abrufen der Dateien von einem (gezogenen) Dateiobjekt in eine Dateieingabe ist das Problem.
Rudie
@Rudie Das Ziehen von Dateien in eine Dateieingabe ist mit Chrome / FF (mithilfe der filesEigenschaft) möglich, aber ich habe es im IE nicht geschafft - hatten Sie Glück?
jlb
1
@jlb Was meinst du mit "Verwenden der Eigenschaft files"? Könnten Sie eine Antwort mit relevantem Code geben? Was ich gesucht habe, funktioniert / existiert in keinem Browser.
Rudie