The trick to styling a file input is that you
-
A) give the style you want to the input’s
label
not theinput
itself (except for hiding it) -
and B) explicitly cause the
label
to delegate aclick
MouseEvent
(rather than the default focus-on-click).
Styled File Input Demo
TL;DR (Short ’n’ Sweet)
-
Hide the original file input by absolutely positioning it off screen:
<input type="file" style="position: absolute; top: -10000px; left: -10000px" />
-
Style the surrounding
label
however you like:<label style=" border: 1px solid red; background-color: lightgray; padding: 1em; cursor: pointer; user-select: none; " >Upload File <input ... /></label>
-
Cause the
label
to delegate to theinput
by itsname
:<label style="..." onclick=" document.body .querySelector('[name=js-file-upload]') .dispatchEvent( new MouseEvent('click') ); " >Upload File <input ... /></label>
How does it work?
Normally an input
will receive focus when you click on its label
- as if you had clicked on the input itself.
<input type="file" />
, however, is a special case. Rather than just focusing the cursor on the input, you actually want a file open dialog to appear. This requires delegation of the click, not just the focus.
Complete Code Example
In this example I’m letting the click
event bubble to the form
and then I catch and delegate it to the input
.
<h1>Styled File-Input Demo</h1>
<form
action=""
onclick="
document.body.querySelector('[name=js-file-upload]').dispatchEvent(
new MouseEvent('click')
);
"
>
<label
for="js-file-upload"
style="
border: 1px solid red;
padding: 1em;
cursor: pointer;
user-select: none;
"
>Upload File
<input
style="position: absolute; top: -10000px; left: -10000px"
name="js-file-upload"
type="file"
multiple
/>
</label>
</form>
Bad Ideas
You may encounter a lot of much fancier ways to do this, with much fancier explanations.
Those are, generally speaking, bad ideas.
Using label
is the semantically correct thing to do - it’s how HTML was designed since the beginning.
It’s boring because it works (and has worked since the 90s), which is great!
This doesn’t require any special frameworks or plugins, and will work better both in browser support and for accessibility than doing the more complex things that go against the standard browser behavior.
References
See also: