Many copies of front-end developers have already been broken about the problem of styling the input field
input [type = file] . The essence of the problem lies in the fact that in
the HTML specification there are no strict rules that establish how the browser should display this element. Moreover, for
input [type = file], there are no attributes that would allow to change its appearance, using CSS styles, you can only change the appearance of its border and font, and using JavaScript, for security reasons, you cannot simulate a click on this element. which would cause a system window to select a file
* . But what to do when a customer wants an adaptive website with beautiful stylized forms, in which this input field cannot be dispensed with?
* - at the time of this writing, I still didn’t know that in all modern browsers, the imitation of a click on
input [type = file] already causes the system file selection window. Many thanks to
lutov for a
valuable comment with a link to the working example from
Pagefest !
Ways to solve the field styling problem
During the time that this problem exists (and it exists for a very long time), several ways were found to solve it. In total there are five:
Method number 1 (the most common)
Convince the customer that you can live with standard
input [type = file] .
')
Method number 2
Write / use a ready file uploader in a flash / java applet. Used, for example, on
habrastorage.orgMethod number 3 (will be discussed in the article)
Using CSS, “mask” the standard
input [type = file] , make it completely transparent and place the stylized fake field in place, so that the click on the latter will click on the standard one and, as a result, open the system window for selecting the file.
Method number 4 new! (will be discussed in the article)
Put the transparent
input [type = file] inside the label element, along with arbitrary stylized inline elements (except for input, button, select and textarea, of course). Clicking on the label will automatically lead to a click and on the hidden field to select the file. Thank you
lampa for a
valuable comment !
Method number 5 new! (will be discussed in the article)
To use imitation of click on hidden
input [type = file] by means of JavaScript. Yes, it already works in all modern browsers. Thanks again
lutov for a
valuable comment !
UPD: Attention , this method is not applicable for Internet Explorer! Despite the fact that the file is selected in a hidden
input [type = file] , when the form is submitted, the value of the latter will be “reset”. Thank you
LeonidFrolov for a
valuable comment !
All the last four ways, of course, have their drawbacks. A significant drawback of the Flash / Java solution is that its work requires the appropriate plug-ins, which may not be available in the user's browser. The big drawback of the “masking” solution is that for its implementation it is necessary to use hacks (it will be discussed below), and also because it is meaningless without the use of JavaScript (after all, you need to somehow distinguish between the “file not selected” states) and the “file is selected” for the stylized fake field, which is impossible to do in one CSS). The decision on JavaScript, in general, would be very good, but, as it turned out in practice, it is not supported by Internet Explorer, as mentioned above. The minus of the label solution is the same use of JavaScript, however, it is much better than the “masking” method and should, in my opinion, be used now to solve this acute problem.
Bicycle scheme
The key task was to create a “rubber”
input [type = file] , which on the screens of mobile devices would be a simple button to select a file (the name of the selected file is displayed on it), and on wide screens it would look like the usual text field + button that can drag on the entire width of the window:
Schematic view of the element on mobile devices
Schematic view of the element on desktop devicesThe article will discuss the last three ways to style the file selection field. Thus, taking into account the scheme specified above, the initial layout for the “masking” method No. 3 will have the following form (the order of the child elements is important!):
<div class="file_upload"> <button type="button"></button> <div> </div> <input type="file"> </div>
Possible layout for the method using the label element:
<label class="file_upload"> <span class="button"></span> <mark> </mark> <input type="file"> </label>
Possible layout for the JavaScript solution (it is the same as the “masking” method):
<div class="file_upload"> <button type="button"></button> <div> </div> <input type="file"> </div>
“Pull, Piglet!” Or styles for “camouflage” mode
So that the reader does not have the wrong impression that each value of CSS properties used in the article is of great importance (the so-called “magic numbers”), we will agree to mark those that can be easily modified to fit your needs with a comment
/* example */
Agreed? Fine! Let's start to stylize our fake file selection field with its “wrapper” - div
.file_upload :
.file_upload{ position: relative; overflow: hidden; font-size: 1em; height: 2em; line-height: 2em }
- the
position
property is set so that relative to the div
.file_upload it is possible to absolutely position its child elements, and the
overflow
property - to hide everything that for some reason does not fit into our wrapper (and there is such a wrapper, but this later). On wide screens, our beautiful field and button should be displayed in one line - let's set a fixed width and float: right for the last, and a small internal indent for the first:
.file_upload > button{ float: right; width: 8em; height: 100% } .file_upload > div{ padding-left: 1em }
Since we want to hide the text field on mobile devices, and there is only one file selection button, you need to set the
media query :
@media only screen and ( max-width: 500px ){ .file_upload > div{ display: none } .file_upload > button{ width: 100% } }
And now - the most fun in this method! It is necessary to make the standard
input [type = file] completely transparent, and stretch it to stretch it to the size of the "wrapper" div
.file_upload . To implement the latter, apply the hack in the form of absolute positioning and the CSS 3
transform
property, with which we will increase the element, for example, by 20 times (yes, this is the most usual “magic number”):
.file_upload input[type=file]{ position: absolute; left: 0; top: 0; width: 100%; height: 100%; transform: scale(20); letter-spacing: 10em; -ms-transform: scale(20); opacity: 0; cursor: pointer }
As you can see from the above CSS fragment, IE 9 required additional crutches. This is due to the fact that when you click on a text field, this browser does not call up the system file selection window, but kindly offers to “erase” the name of the already selected one, which is symbolized by a flashing text cursor. Therefore, it additionally sets a huge spacing between letters, which increases the button of the element to the size of the div
.file_upload . I also note that the z-index is not indicated in this case, since the element is the last “descendant” in the markup selected from the very beginning.
Using the example of the FireFox desktop browser, our custom file selection field for different window sizes now looks like this:


“All ingenious is easy!” Or styles for a method using a label
The main styles applied to the text field and the button for this method are similar to those already discussed above:
.file_upload{ display: block; position: relative; overflow: hidden; font-size: 1em; height: 2em; line-height: 2em } .file_upload .button, .file_upload > mark{ display: block; cursor: pointer } .file_upload .button{ float: right; box-sizing: border-box; -moz-box-sizing: border-box; width: 8em; height: 100%; text-align: center } .file_upload > mark{ background: transparent; padding-left: 1em }
However, now there is no need to use the hack with the "stretching" of the transparent
input [type = file] :
.file_upload input[type=file]{ position: absolute; top: 0; opacity: 0 }
“How does it work?” Or styles for a JavaScript solution
Since the initial layout for this method was chosen the same as in the “camouflage”, the styles for the button and the text field for both methods also coincide (except, perhaps, the
cursor: pointer
properties, which, in this case, will be applied to button and text box). The
input [type = file] style can be the same as that used in the method with the use of the label element, but it is better to use
visibility
instead of the
opacity
property:
.file_upload input[type=file]{ position: absolute; top: 0; visibility: hidden }
Need more styles!
Of course, in such a primitive form, the file selection field is unlikely to suit anyone, so add additional styles that make the file select button, say, purple, add shadows, etc. Do not forget to add your own style for the button, when the cursor is over it, the style for the pressed button, and also add a style for the whole element when the focus is on it (to be applied using JavaScript):
.file_upload{ border: 1px solid #ccc; border-radius: 3px; box-shadow: 0 0 5px rgba(0,0,0,0.1); transition: box-shadow 0.1s linear } .file_upload.focus{ box-shadow: 0 0 5px rgba(0,30,255,0.4) } .file_upload > button{ background: #7300df; transition: background 0.2s; border: 1px solid rgba(0,0,0,0.1); border-color: rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25); border-radius: 2px; box-shadow: 0 1px 0 rgba(255, 255, 255, 0.2) inset, 0 1px 2px rgba(0, 0, 0, 0.05); color: #fff; text-shadow: #6200bd 0 -1px 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis } .file_upload:hover > button{ background: #6200bd; text-shadow: #5d00b3 0 -1px 0 } .file_upload:active > button{ background: #5d00b3; box-shadow: 0 0 3px rgba(0,0,0,0.3) inset }
Now our file selection field looks like this:


Need more crutches!
Since we make a full field for file selection, we need to ensure that it can be comfortably filled from the keyboard (for the “masking” method, the focus is now first set on the stylized button, and then on the hidden
input [type = file] , which does not visually manifest). For this, of course, use JavaScript. In order not to write a lot of code, I allow myself to use the popular jQuery library. Then, for the "camouflage" method:
var wrapper = $( ".file_upload" ), inp = wrapper.find( "input" ), btn = wrapper.find( "button" ), lbl = wrapper.find( "div" ); btn.focus(function(){ inp.focus() });
For the method using the label, you can remove a section of code that is responsible for the forced transfer of focus from the button to
input [type = file] (since we do not have a button at all, but a span).
For a method whose essence is to simulate a click on
input [type = file] , you need to add this simulation itself:
and also correct the code for setting the
.focus class, after removing the fragment responsible for the forced transfer of focus:
The input field has so far remained “dead” - when choosing a file, the name of the latter was not displayed anywhere. The time has come to fix this:
var file_api = ( window.File && window.FileReader && window.FileList && window.Blob ) ? true : false; inp.change(function(){ var file_name; if( file_api && inp[ 0 ].files[ 0 ] ) file_name = inp[ 0 ].files[ 0 ].name; else file_name = inp.val().replace( "C:\\fakepath\\", '' ); if( ! file_name.length ) return; if( lbl.is( ":visible" ) ){ lbl.text( file_name ); btn.text( "" ); }else btn.text( file_name ); }).change();
- if the browser supports File API, then the file name is determined using it, otherwise it is cut from the value of the hidden
input [type = file] . For mobile devices, when an item is a single button, the name of the selected file is displayed on it.
It would seem that all that is required is already written. But figushki! If you select a file using the “mobile” field, and then increase the window size and transfer the element to the “desktop”, then the “File not selected” will remain in the text field - you need to update the element each time the window is resized:
$( window ).resize(function(){ $( ".file_upload input" ).triggerHandler( "change" ); });
And what did we get in the end?
The resulting stylized file selection field was successfully tested for all three methods in the following browsers:
- FireFox 22.0 (Linux, Windows)
- Opera 12.16 (Linux, Windows)
- Internet Explorer 9
- Chromium 27.0 (Linux)
- Apple Safari (iOS 6.3.1)
- Android browser (Android 2.3.6)
- Android FireFox
Of the advantages of all the approaches considered in the article are the following main ones:
- Flash is not used.
- The element can be easily styled with CSS using modern technologies of responsive design.
- The field can be filled with the keyboard.
Of the main disadvantages:
- The need to use javascript (applies to all methods).
- Using CSS hacks for “masking” mode.
- The need to write additional crutches for the field with the multiple attribute (applies to all methods).
- Non-cross-browser compatibility - all methods lack IE 8 support, and you also need to use “browser-based” CSS properties to support the rest of the “oldies”.
- The JavaScript solution is not supported by all versions of Internet Explorer, as well as by some old versions of other popular browsers (although almost no one uses them anymore).
Which of the two two elegant ways to create stylized
input [type = file] to choose for everyday use is up to you. My choice is the label method (although it has a non-semantic layout).
Working examples of all three methods can be found at CodePen:
"Camouflage" methodMethod using labelMethod with a simulated click on JavaScript