Alles fließt, alles ändert sich, aber nur die
input[type=file]
hat die Nerven aller unerfahrenen Webentwickler verwöhnt und tut dies bis jetzt. Erinnern Sie sich an sich selbst vor N Jahren, als Sie gerade erst begannen, die Grundlagen der Erstellung von Websites zu verstehen. Jung und unerfahren, waren Sie wirklich überrascht, als sich die Schaltfläche zur Dateiauswahl vollständig weigerte, die Hintergrundfarbe in Ihren Lieblingspfirsich zu ändern. In diesem Moment sind Sie zum ersten Mal auf diesen unzerstörbaren Eisberg namens "Downloading Files" gestoßen, der bis heute unerfahrene Webentwickler "ertränkt".
Am Beispiel des Erstellens eines Datei-Upload-Felds werde ich Ihnen zeigen, wie Sie die
input[type=file]
korrekt ausblenden, den Fokus auf ein Objekt ohne Fokus setzen, Drag & Drop-Ereignisse verarbeiten und Dateien über AJAX senden. Außerdem werde ich Ihnen einige Browser-Fehler und Möglichkeiten zur Umgehung vorstellen. Der Artikel ist für Anfänger geschrieben, kann aber an einigen Stellen auch für erfahrene Entwickler nützlich und unterhaltsam sein.
Markup und primäre Stile
Beginnen wir mit dem HTML-Markup:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title> , </title> <link rel="stylesheet" href="style.css"> <script type="text/javascript" src="jquery-3.3.1.min.js"></script> <script type="text/javascript" src="script.js"></script> </head> <body> <form id="upload-container" method="POST" action="send.php"> <img id="upload-image" src="upload.svg"> <div> <input id="file-input" type="file" name="file" multiple> <label for="file-input"> </label> <span> </span> </div> </form> </body> </html>
Vielleicht ist das Hauptelement, auf das Sie achten sollten,
<label for="file-input"> </label>
Die HTML-Spezifikation erlaubt es uns nicht, visuelle Eigenschaften direkt für die
input[type=file]
, aber wir haben ein
label
Tag, auf das geklickt wird, wodurch ein Klick auf das Formularelement ausgelöst wird, an das es angehängt ist. Zu unserer Freude hat dieses Tag keine Einschränkungen bei der Stilisierung: Wir können damit machen, was wir wollen.
Es entsteht ein Aktionsplan: Wir stilisieren das Etikett nach Belieben und verbergen
input[type=file]
außer Sichtweite. Richten Sie zunächst die allgemeinen Seitenstile ein:
body { padding: 0; margin: 0; display: flex; justify-content: center; align-items: center; min-height: 100vh; } #upload-container { display: flex; justify-content: center; align-items: center; flex-direction: column; width: 400px; height: 400px; outline: 2px dashed #5d5d5d; outline-offset: -12px; background-color: #e0f2f7; font-family: 'Segoe UI'; color: #1f3c44; } #upload-container img { width: 40%; margin-bottom: 20px; user-select: none; }
Gestalten Sie jetzt unser Etikett:
#upload-container label { font-weight: bold; } #upload-container label:hover { cursor: pointer; text-decoration: underline; }
Was wir anstreben (
input[type=file]
aus dem Markup entfernt):

Natürlich können Sie die Beschriftung zentrieren, einen Hintergrund und einen Rahmen hinzufügen und eine vollständige Schaltfläche erhalten, aber unsere Priorität ist Drag & Drop.
Eingabe ausblenden
Jetzt müssen wir die
input[type=file]
ausblenden. Das erste, was den Kopf trifft, ist die
display: none
und
visibility: hidden
Eigenschaften. Das ist aber nicht so einfach. In einigen älteren Browsern hat das Klicken auf das Etikett keine Auswirkungen mehr. Das ist aber noch nicht alles. Wie Sie wissen, können unsichtbare Elemente keinen Fokus erhalten, und unabhängig davon, was sie sagen, ist der Fokus wichtig, da dies für manche Menschen die einzige Möglichkeit ist, mit der Site zu interagieren. Diese Methode passt also nicht zu uns. Lassen Sie uns das umgehen:
#upload-container div { position: relative; z-index: 10; } #upload-container input[type=file] { width: 0.1px; height: 0.1px; opacity: 0; position: absolute; z-index: -10; }
Wir positionieren unsere
input[type=file]
absolut relativ zu ihrem übergeordneten Block, reduzieren sie auf
0.1px
, machen sie transparent und setzen ihren
z-index
kleiner als den des übergeordneten Blocks, sozusagen sicher.
Herzlichen Glückwunsch, wir haben erreicht, was wir wollten: Unser Feld sieht genauso aus wie auf dem vorherigen Bild.
Passen Sie den Fokus an
Da unsere
input[type=file]
physisch auf der Seite vorhanden ist, kann sie den Fokus erhalten. Das heißt, wenn wir die
Tab
auf der Seite drücken, wechselt der Fokus irgendwann zur
input[type=file]
. Das Problem ist jedoch, dass wir dies nicht sehen werden: Das Feld, das wir versteckt haben, wird auffallen. Ja, wenn wir in diesem Moment die
Enter
drücken, wird das Dialogfeld geöffnet und alles funktioniert wie es sollte. Aber wie verstehen wir, dass es Zeit ist, zu drücken?
Unsere Aufgabe ist es, eine Marke in dem Moment auf eine bestimmte Weise auszuwählen, in dem der Fokus auf dem Feld zum Hochladen von Dateien liegt. Aber wie können wir das tun, wenn das Tag keinen Fokus erhalten kann? CSS3-Kenner werden sofort an die Pseudoklasse denken
:focus
, die Stile für Elemente im Fokus definiert, und
+
oder
~
Selektoren, die die richtigen Nachbarn auswählen: Elemente, die sich auf derselben Verschachtelungsebene befinden und nach dem ausgewählten Element stehen. In Anbetracht dessen, dass sich in unserer Markup-
input[type=file]
direkt vor dem
label
Tag befindet, erfolgt der folgende Eintrag:
#upload-container input[type=file]:focus + label { }
Aber nicht so einfach. Lassen Sie uns zunächst diskutieren, wie wir ein Etikett auswählen sollen. Wie Sie wissen, haben alle modernen und nicht so Browser eindeutige Standardeigenschaften für Elemente im Fokus. Grundsätzlich ist dies die
outline
, die einen Strich um ein Element erzeugt, der sich vom
border
unterscheidet, dass die Größe des Elements nicht geändert wird und von diesem weg verschoben werden kann. In der Regel verwenden Benutzer nur einen Browser, um sich an dessen Standards zu gewöhnen. Um den Benutzern das Navigieren auf unserer Website zu erleichtern, sollten wir versuchen, den Fokus so anzupassen, dass er für die meisten gängigen modernen Browser so natürlich wie möglich aussieht. Mit JavaScript können Sie theoretisch Informationen darüber abrufen, in welchem Browser der Benutzer die Site geöffnet hat, und die Stile entsprechend anpassen. Dieses Thema ist jedoch als Teil eines Artikels, der hauptsächlich für Anfänger gedacht ist, zu komplex und umständlich. Wir werden versuchen, mit etwas Blut auszukommen.
In Browsern, die auf der WebKet-Engine basieren (Google Chrome, Opera, Safari), hat die Standardeigenschaft für Elemente im Fokus die Form:
:focus { outline: -webkit-focus-ring-color auto 5px; }
Hier ist
-webkit-focus-ring-color
die nur für diese Engine spezifische Fokus-Strich-
-webkit-focus-ring-color
. Das heißt, diese Zeile funktioniert ausschließlich in WebKit-Browsern, und genau das benötigen wir. Geben Sie diese Eigenschaft für unser Label an:
#upload-container input[type=file]:focus + label { outline: -webkit-focus-ring-color auto 5px; }
Wir öffnen Google Chrome oder Opera, wir schauen. Alles funktioniert wie es sollte:

Mal sehen, wie die Dinge in Mozilla Firefox und Microsoft Edge im Fokus stehen. Für diese Browser lautet die Standardeigenschaft:
:focus { outline: 1px solid #0078d7; }
und
:focus { outline: 1px solid #212121; }
entsprechend.
Leider
-moz-
Präfix
-moz-
nicht mit der
outline
. Daher müssen wir auswählen, welche dieser beiden Eigenschaften wir wählen. Da die Anzahl der Firefox-Benutzer viel höher ist, ist es rationaler, diesem bestimmten Browser den Vorzug zu geben. Dies bedeutet nicht, dass wir Edge-Benutzern und anderen Browsern die Möglichkeit nehmen, zu sehen, wo der Fokus jetzt liegt. Es sieht einfach „anders“ aus als sie. Nun, du musst Opfer bringen.
Wir fügen den Stil von Mozilla Firefox vor dem Stil für WebKit hinzu: Zuerst wenden alle Browser die erste Eigenschaft an, und dann wenden diejenigen, die dies können (Google Chrome, Opera, Safari usw.), die zweite an.
#upload-container input[type=file]:focus + label { outline: 1px solid #0078d7; outline: -webkit-focus-ring-color auto 5px; }
Und hier beginnt das Seltsame: In Edge funktioniert alles einwandfrei, aber Firefox weigert sich aus unbekannten Gründen, Eigenschaften auf das Etikett anzuwenden, wobei der Schwerpunkt auf der
input[type=file]
. Und das Fokusereignis selbst passiert - überprüft durch JavaScript. Wenn Sie den Fokus auf das Dateiauswahlfeld über die Entwicklertools erzwingen, wird die Eigenschaft angewendet und unser Strich wird angezeigt! Anscheinend ist dies ein Fehler des Browsers selbst, aber wenn jemand Ideen hat, warum dies passiert, schreiben Sie in die Kommentare.
Naja, nichts, normale Helden gehen immer herum. Wie ich bereits sagte, tritt das Fokusereignis auf, was bedeutet, dass wir die Eigenschaften direkt aus JavaScript anpassen können. Dafür müssen wir jedoch die Logik unseres Selektors ändern:
#upload-container label.focus { outline: 1px solid #0078d7; outline: -webkit-focus-ring-color auto 5px; }
Wir werden die
.focus
Klasse für unser Label beschreiben und sie jedes Mal hinzufügen, wenn die
input[type=file]
Fokus erhält, und sie entfernen, wenn sie verliert.
$('#file-input').focus(function() { $('label').addClass('focus'); }) .focusout(function() { $('label').removeClass('focus'); });
Jetzt funktioniert alles wie es sollte. Herzlichen Glückwunsch, wir haben den Fokus herausgefunden.
Drag & Drop
Die Arbeit mit Drag & Drop erfolgt durch Verfolgen spezieller Browserereignisse:
drag, dragstart, dragend, dragover, dragenter, dragleave, drop
. Eine detaillierte Beschreibung der einzelnen Elemente finden Sie im Internet. Wir werden nur einige von ihnen verfolgen.
Definieren Sie zunächst ein Drag & Drop-Element:
var dropZone = $('#upload-container');
Dann beschreiben wir in CSS eine spezielle Klasse, die wir
dropZone
zuweisen, wenn sich der Cursor, der die Datei zieht, direkt darüber befindet. Dies ist erforderlich, um den Benutzer visuell darüber zu informieren, dass die Datei bereits freigegeben werden kann.
#upload-container.dragover { background-color: #fafafa; outline-offset: -17px; }
Gehen wir nun zur JS-Datei. Um zu beginnen, müssen wir alle Standardaktionen für Drag & Drop-Ereignisse rückgängig machen. Eines dieser Ereignisse ist beispielsweise das Öffnen einer von einem Browser ausgelösten Datei. Wir brauchen das überhaupt nicht, also werden wir die folgenden Zeilen schreiben:
dropZone.on('drag dragstart dragend dragover dragenter dragleave drop', function(){ return false; });
In jQuery entspricht das Aufrufen der
return false
dem gleichzeitigen Aufrufen von zwei Funktionen:
e.preventDefault()
und
e.stopPropagation()
.
Wir beginnen, unseren eigenen Event-Handler zu beschreiben. Wir werden
dragenter
dragover
wie mit Fokus, aber dieses Mal werden wir die Ereignisse von
dragenter
und
dragover
, um eine Klasse hinzuzufügen, und das
dragleave
Ereignis, um sie zu entfernen:
dropZone.on('dragover dragenter', function() { dropZone.addClass('dragover'); }); dropZone.on('dragleave', function(e) { dropZone.removeClass('dragover'); });
Und wieder erwartet uns eine unangenehme Überraschung: Wenn Sie sich mit der Maus mit der Datei entlang der
dropZone
bewegen, beginnt das Feld zu flackern. Dies geschieht in Microsoft Edge- und WebKit-Browsern. Übrigens laufen die meisten dieser WebKit-Browser derzeit auf der Blink-Engine (ich schätze die Ironie, oder?). Aber in Mozilla flackert nichts. Anscheinend habe ich beschlossen, es nach Fokusfehlern zu beheben.
Und dieses Flimmern tritt auf, weil das
dragleave
Ereignis aus irgendeinem Grund
dragleave
, wenn Sie mit der
dropZone
über das
dropZone
, sei es ein Bild oder ein
div
mit einem Dateiauswahlfeld und einer Beschriftung. Uns ist klar, dass wir das Feld nicht verlassen, die Browser jedoch aus irgendeinem Grund nicht, und aus diesem Grund entfernen sie
.focus
die
.focus
Klasse aus
dropZone
.
Und wieder müssen wir irgendwie raus. Wenn der Browser selbst nicht versteht, dass wir das Feld nicht verlassen, müssen wir ihm helfen. Und wir werden dies durch zusätzliche Bedingungen tun: Wir berechnen die Koordinaten der Maus relativ zu
dropZone
und prüfen dann, ob sich der Cursor außerhalb des Blocks befindet. Wenn links, entfernen wir den Stil:
dropZone.on('dragleave', function(e) { let dx = e.pageX - dropZone.offset().left; let dy = e.pageY - dropZone.offset().top; if ((dx < 0) || (dx > dropZone.width()) || (dy < 0) || (dy > dropZone.height())) { dropZone.removeClass('dragover'); }; });
Und das ist alles, das Problem ist gelöst! So sieht unser Feld mit der darin enthaltenen Datei aus:

Fahren wir mit der Behandlung des
drop
Ereignisses selbst fort. Denken Sie jedoch zunächst daran, dass wir zusätzlich zu Drag & Drop eine
input[type=file]
haben und jede dieser Methoden im Wesentlichen unabhängig ist, jedoch dieselben Aktionen ausführen muss: Dateien hochladen. Daher schlage ich vor, für beide Methoden eine separate universelle Funktion zu erstellen, in die wir Dateien übertragen, und die bereits entscheidet, was mit ihnen geschehen soll. Wir werden es
sendFiles()
, aber wir werden es etwas später beschreiben. Behandeln Sie zunächst das
drop
Ereignis:
dropZone.on('drop', function(e) { dropZone.removeClass('dragover'); let files = e.originalEvent.dataTransfer.files; sendFiles(files); });
Entfernen
.dragover
die
.dragover
Klasse aus
dropZone
. Dann erhalten wir ein Array mit den Dateien. Wenn Sie jQuery verwenden,
e.originalEvent.dataTransfer.files
der Pfad
e.originalEvent.dataTransfer.files
. Wenn Sie in reinem JS schreiben,
e.dataTransfer.files
. Nun, dann übergeben wir das Array an unsere noch nicht realisierte Funktion.
Jetzt erarbeiten wir die Lademethode über die
input[type=file]
:
$('#file-input').change(function() { let files = this.files; sendFiles(files); });
Wir verfolgen das
change
auf der Schaltfläche zur Dateiauswahl,
this.files
das Array über
this.files
und senden es an die Funktion.
Senden von Dateien über AJAX
Der letzte Schritt - die Beschreibung der Dateiverarbeitungsfunktion - ist für alle einzigartig. Es hängt direkt von dem Ziel ab, das Sie verfolgen. Als Beispiel werde ich zeigen, wie Dateien über AJAX an den Server gesendet werden.
Angenommen, wir erstellen ein Feld zum Hochladen von Fotos. Wir möchten nicht, dass etwas anderes auf unseren Server gelangt, daher entscheiden wir uns für Dateitypen: Sei es PNG und JPEG. Es lohnt sich auch, die maximale Größe eines Fotos zu regeln, das ein Benutzer senden kann. Begrenzt auf fünf Megabyte. Wir beginnen unsere Funktion zu beschreiben:
function sendFiles(files) { let maxFileSize = 5242880; let Data = new FormData(); $(files).each(function(index, file) { if ((file.size <= maxFileSize) && ((file.type == 'image/png') || (file.type == 'image/jpeg'))) { Data.append('images[]', file); } }); };
In der Variablen
maxFileSize
maximale Dateigröße ein, die an den Server gesendet wird.
FormData()
-Funktion erstellen wir ein neues Objekt der
FormData
Klasse, mit dem wir Sätze von Schlüssel-Wert-Paaren generieren können. Ein solches Objekt kann einfach über AJAX gesendet werden. Als Nächstes verwenden wir das Konstrukt jQuery
.each
für das
.each
, das die Funktion anwendet, die wir für jedes seiner Elemente festgelegt haben. Die Nummer des Elements und das Element selbst werden als Argumente an die Funktion übergeben, die wir als
index
bzw.
file
. In der Funktion selbst überprüfen wir die Datei anhand unserer Kriterien: Die Größe beträgt weniger als fünf Megabyte und der Typ ist PNG oder JPEG. Wenn die Datei den Test besteht, fügen Sie sie unserem
FormData
Objekt hinzu, indem Sie die Funktion
append()
aufrufen. Der Schlüssel ist die Zeichenfolge
'photos[]'
, deren eckige Klammern angeben, dass es sich um ein Array handelt, in dem sich mehrere Objekte befinden können. Das Objekt selbst wird eine
file
.
Jetzt ist alles bereit, um Dateien über AJAX zu senden. Fügen Sie unserer Funktion die folgenden Zeilen hinzu:
$.ajax({ url: dropZone.attr('action'), type: dropZone.attr('method'), data: Data, contentType: false, processData: false, success: function(data) { alert(' '); } });
Als Parameter
url
und
type
wir die Werte der
action
und
method
der
input[type=file]
an. Durch AJAX werden wir ein
Data
. Die Parameter
contentType: false
und
processData: false
werden benötigt, damit der Browser unsere Dateien nicht versehentlich in ein anderes Format übersetzt. Im
success
geben wir die Funktion an, die ausgeführt wird, wenn die Dateien erfolgreich auf den Server übertragen werden. Der Inhalt hängt von Ihrer Vorstellungskraft ab, aber ich beschränke mich auf eine bescheidene Ausgabe einer Nachricht über einen erfolgreichen Download.
Herzlichen Glückwunsch, jetzt können Sie Ihr eigenes Feld zum Hochladen von Dateien erstellen! Natürlich positioniere ich meine Methode nicht als die einzig wahre und richtige. Mein Ziel war es, den allgemeinen Weg zur Lösung dieses Problems aufzuzeigen, der vor allem für Anfänger geeignet ist. Wenn Sie denken, dass Sie irgendwo etwas Besseres tun könnten - schreiben Sie in die Kommentare, wir werden diskutieren!
Das ist alles. Vielen Dank für Ihre Aufmerksamkeit!
Herunterladen:
- Endgültige Version
- Fokusproblem
- Flackerndes Problem
Berühren Sie:
- Endgültige Version
- Fokusproblem
- Flackerndes Problem