input[type=file]
as it spoiled the nerves of all novice web developers, and continues to do so until now. Remember yourself N years ago, when you were just beginning to comprehend the basics of creating websites. Young and inexperienced, you were genuinely surprised when the file selection button completely refused to change the color of its background to your favorite peach. It was at that moment that you first encountered this invincible iceberg called “Uploading Files”, which to this day continues to “drown” novice web developers.input[type=file]
correctly, adjust the focus on an object that cannot focus, process Drag-and-Drop events and send files via AJAX. And also I will introduce you to a couple of browser bugs and ways to work around them. The article is written for beginners, but in some moments it can be useful and entertaining even for experienced developers. <!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>
<label for="file-input"> </label>
input[type=file]
, but we have a label
tag, clicking on which causes a click on the form element to which it is attached. To our joy, this tag has no limitations in styling: we can do whatever we want with it.input[type=file]
out of sight. First, let's set up the general page styles: 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; }
#upload-container label { font-weight: bold; } #upload-container label:hover { cursor: pointer; text-decoration: underline; }
input[type=file]
removed from markup):input[type=file]
. The first thing that catches your head is the display: none
and visibility: hidden
properties. But it is not so simple. On some older browsers, clicking on the tag will stop producing any effect. But that's not all. As you know, invisible elements can not get the focus, and no matter what they say, the focus is important, because for some people this is the only way to interact with the site. So this method does not suit us. Let's take a detour: #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; }
input[type=file]
relative to its parent unit, reduce it to 0.1px
, make it transparent and set its z-index
less than that of the parent, so that, so to speak, for sure.input[type=file]
physically present on the page, it has the ability to receive focus. That is, if we press the Tab
key on the page, then at some point the focus will shift to input[type=file]
. But the problem is that we will not see it: there will be a field that we have hidden. Yes, if at this moment we press Enter
, then the dialog box will open and everything will work as it should, but how do we understand that it is time to press?:focus
, which defines styles for elements in focus, and selectors +
or ~
, which choose right neighbors: elements located at the same level of nesting after the selected element. If we consider that in our markup, input[type=file]
is located right in front of the label
tag, the following entry takes place: #upload-container input[type=file]:focus + label { /* */ }
outline
property, which creates a stroke around the element that differs from the border
in that it does not change the size of the element and can be moved away from it. As a rule, people use only one browser, so they get used to its standards. To make it easier for people to navigate on our site, we must try to adjust the focus so that it looks as natural as possible for most popular modern browsers. In theory, using JavaScript, you can get information about the browser through which the user opened the site, and adjust the styles accordingly, but this article is too complex and cumbersome as part of an article intended primarily for beginners. We will try to do a little blood. :focus { outline: -webkit-focus-ring-color auto 5px; }
-webkit-focus-ring-color
is a focal stroke -webkit-focus-ring-color
specific only for this engine. That is, this line will work exclusively in WebKit-browsers, and this is exactly what we need. We indicate this property for our label: #upload-container input[type=file]:focus + label { outline: -webkit-focus-ring-color auto 5px; }
:focus { outline: 1px solid #0078d7; }
:focus { outline: 1px solid #212121; }
-moz-
prefix with the outline
property will not work. Therefore, we have to choose which of these two properties we choose. Since the number of Firefox users is much higher, it is more rational to give preference to this particular browser. This does not mean that we are depriving Edge users and other browsers of the opportunity to see where the focus is now, it’s just that they will look like “non-native”. Well, you have to make sacrifices. #upload-container input[type=file]:focus + label { outline: 1px solid #0078d7; outline: -webkit-focus-ring-color auto 5px; }
input[type=file]
. And the focus
event itself happens - checked through JavaScript. Moreover, if we forcefully set the focus on the file selection field through the developer tools, the property will be applied and our stroke will appear! Apparently, this is a bug of the browser itself, but if someone has an idea why this happens, write in the comments.focus
event happens, which means we can adjust the properties directly from JavaScript. But for this we will have to change the logic of our selector: #upload-container label.focus { outline: 1px solid #0078d7; outline: -webkit-focus-ring-color auto 5px; }
.focus
for our label and we will add it every time when input[type=file]
gets the focus and remove when it loses. $('#file-input').focus(function() { $('label').addClass('focus'); }) .focusout(function() { $('label').removeClass('focus'); });
drag, dragstart, dragend, dragover, dragenter, dragleave, drop
. A detailed description of each of them you can easily find on the Internet. We will track only some of them. var dropZone = $('#upload-container');
dropZone
when the cursor that pulls the file is directly above it. This is necessary to visually inform the user that the file can already be released. #upload-container.dragover { background-color: #fafafa; outline-offset: -17px; }
dropZone.on('drag dragstart dragend dragover dragenter dragleave drop', function(){ return false; });
return false
equivalent to calling two functions at once: e.preventDefault()
and e.stopPropagation()
.dragenter
and dragover
to add a class and the dragleave
event to remove it: dropZone.on('dragover dragenter', function() { dropZone.addClass('dragover'); }); dropZone.on('dragleave', function(e) { dropZone.removeClass('dragover'); });
dropZone
with the mouse with the file, the field starts to flicker. It happens in Microsoft Edge and WebKit-browsers. By the way, most of these WebKit browsers currently run on the Blink engine (appreciated the irony, huh?). But in Mozilla nothing flickers. Apparently, I decided to correct after the bugs with the focus.dropZone
child element, be it a picture or a div
with a file selection field and a label, for some reason, the dragleave
event is dragleave
. It is obvious to us that we are not leaving the field, but for some reason, there is no browsers, and because of this, they .focus
the dropZone
class from dropZone
.dropZone
, and then we will check if the cursor is outside the block. If left, then remove the style: 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'); }; });
drop
event itself. But first, remember that, in addition to Drag-and-Drop, we have input[type=file]
, and each of these methods is independent in its essence, but must perform the same actions: upload files. Therefore, I propose to create a separate function, universal for both methods, to which we will transfer files, and it will already decide what to do with them. Let's call it sendFiles()
, but we sendFiles()
describe it a bit later. First, let's handle the drop
event: dropZone.on('drop', function(e) { dropZone.removeClass('dragover'); let files = e.originalEvent.dataTransfer.files; sendFiles(files); });
.dragover
class from the dropZone
. Then we get an array containing the files. If you use jQuery, then the path will be e.originalEvent.dataTransfer.files
, if you write in pure JS, then e.dataTransfer.files
. Well, then we pass the array to our as yet unimplemented function.input[type=file]
: $('#file-input').change(function() { let files = this.files; sendFiles(files); });
change
event on the file selection button, get an array through this.files
and send it to the function. 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); } }); };
maxFileSize
variable, we maxFileSize
maximum file size that we will send to the server. FormData()
function, we will create a new object of the FormData
class, allowing us to form sets of key-value pairs. Such an object can be easily sent via AJAX. Next, we use the jQuery .each
construction for the files
array, which will apply the function we set for each of its elements. As arguments, the function will be the sequence number of the element and the element itself, which we will process as index
and file
respectively. In the function itself, we check the file for compliance with our criteria: the size is less than five megabytes, and the type is PNG or JPEG. If the file passes the check, then add it to our FormData
object by calling the append()
function. The key is the string 'photos[]'
, the square brackets at the end of which denote that it is an array in which there can be several objects. The object itself will be file
. $.ajax({ url: dropZone.attr('action'), type: dropZone.attr('method'), data: Data, contentType: false, processData: false, success: function(data) { alert(' '); } });
url
and type
parameters, we specify the values ​​of the action
and method
attributes for input[type=file]
respectively. We will transmit the Data
object via AJAX. Parameters contentType: false
and processData: false
are needed so that the browser does not accidentally transfer our files to some other format. In the success
parameter we indicate the function that will be executed if the files are successfully transferred to the server. Its content depends on your imagination, but I will confine myself to the modest output of a message about a successful download.Source: https://habr.com/ru/post/423035/
All Articles