Das Feld zum Hochladen von Dateien, das wir verdienen

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:

  1. Endgültige Version
  2. Fokusproblem
  3. Flackerndes Problem

Berühren Sie:

  1. Endgültige Version
  2. Fokusproblem
  3. Flackerndes Problem

Source: https://habr.com/ru/post/de423035/


All Articles