Ever wanted to style the input with type="file"
form control, and have a mini image preview in addition to that, you are in the right place.
Today, we will walk through how to make that specific input element accessible and beautiful.
Before that, a brief introduction:
Introduction: Forms Ubiquity
Forms are one of the key components of the web. They are everywhere. If you want to login or sign up for a service, you interact with a form. If you need to provide feedback for a product, your data is collected with a form.
It is therefore paramount that as users and designers of forms, we should work towards making sure, they aren't a hassle to fill in. They are easy to understand, and the user leaves with the reassurance that they have filled in the appropriate details.
If you need a deep dive into how to create accessible forms, web.dev by Google developers has a wonderful module on forms. You can access it by clicking here
For today, we are going to be working with the input element. Specifically, the input element of type="file"
. This input element is used to help users select and upload files from their devices. It could be mobile or desktop.
One issue with this input form control though is: it is very difficult to style. The default look of it when you create an input element is this:
<input type="file></input>
It is easy to understand. Yet, not exactly how we want it to be.
We want the label styles to look like a button and also show an image Preview. Let's get right on to it.
The whole project can be found on JSFiddle. If you are interested in seeing the final result, click the Link to JSFiddle playground
If you do want to code along from scratch, let's get started.
This project is going to be divided into three sections:
- The Markup
- The Styling
- Interactivity with JavaScript
The Markup
The Markup is the HTML we have to work with. The required elements we need are:
<div class="container">
<label for="file" id="file-upload">Select an Image</label>
<input type="file" name="file-upload"
id="file" accepts=".jpg, .jpeg, .png">
<div class="preview">
<img id="file-preview">
</div>
</div>
Let's look at what this markup is all about. We create a div with a class of Container. This div is going to contain both our form control -- the input of type="file" -- and the image element we need to display as a preview.
Next is the label for the input element. For accessibility, input fields should always have a label that describes the form control. Here the label has a for attribute of file which corresponds to the id of the input element.
Just think of it this way, the label for attribute tells us what form it is linked to. The form it is linked to will have the same value in its id.
so label for="file" tells screen readers that it is related to the input id="file". Remember that attributes values are case sensitive. file is not the same as File. that could potentially trip you up so be wary of it.
Next we have our form control with a name and an id. We add the accepts attribute that tells us what can be uploaded. Here it is delimited to files that have the .jpg
, .jpeg
or .png extension. That attribute is optional and you can safely take it out.
The next line of code is our container with the class of preview. This container will hold our image element inside of it. The image element doesn't have a src
nor alt
attribute...yet. It has an id. We are going to insert the src
and alt attribute with the help of JavaScript.
Once you have all that done, you should have this:
We have our basic form control. Next, onto styling.
The Styling
CSS is always fun to write. We are going to be doing quite a lot to transform our meh input form control to an attractive button.
Let's get started:
First, we give our body element, a height of 100vh
body{
height: 100vh;
}
Next, we style our container.
.container {
width: 100vh;
height: 100vw;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
We give the container, a height and width property. Next we add the display: flex
property to align the children of the container to the center of it. The flex-direction
changes the orientation of the children from row to column
With that, we should have this:
The next step is to style the form control. Input fields of type file are not exactly easy to style so we are going to style the label element instead. Remember, the for attribute we added before. Now it is going to come in handy. Because we set a for attribute on the label element, we can visually hide the input element from the screen and yet still trigger the corresponding action. Let's see that in code
label {
background: hotpink;
padding: 15px 20px;
border-radius: 5px;
font-weight: bold;
color: white;
text-transform: uppercase;
cursor: pointer;
font-family: Avenir, Helvetica, Arial, sans-serif;
font-size: 11px;
box-shadow: 0 3px 10px 0 rgba(0,0,0,0.2), 0 6px 20px 0 rgba(0,0,0,0.19);
transition: box-shadow ease 0.25s;
}
label:active {
box-shadow:none;
}
We have basic button styles. We give it a background
of hotpink
-- because hotpink
is the best color -- padding
, border-radius
etc.
The box-shadow
is to make our button have this push down effect. We would get to that soon. the active pseudo-class
is added to the label
element in order to remove the box-shadow
when it is active.
Quick Info on the active pseudoclass by MDN:
The :active CSS pseudo-class represents an element (such as a button) that is being activated by the user. When using a mouse, "activation" typically starts when the user presses down the primary mouse button.
With all that added, we should have this:
The input
element is still present. We can style this by either using the opacity
property or making it non-visible. The opacity
property is recommended because, according to MDN:
Note: opacity is used to hide the file input instead of visibility: hidden or display: none, because assistive technology interprets the latter two styles to mean the file input isn't interactive.
We still want assistive technologies to know that we can interact with the form control. The way I did it was to just make the input
element miniscule. Here's the code:
input#file {
width: 0;
height: 0;
}
Here it is in motion:
We have that nice click effect.
For the image element, we can hide it for now. You can do that by using the line of code below:
img{
width: 50px;
height: 50px;
display: none;
}
.preview{
margin-top: 20px;
position: relative;
left: -50px;
width: 50px;
height: 50px;
}
That is all for our button styles and our image element.
We are going to style the container with the class of preview for a couple of reasons. First, we want a margin
to separate our image from the button. We also wouldn't want any jarring layout shift so we set an extrinsic size with the width
and height
property.
The position
property is to align it along the left margin of the button.
All that's left to add is JavaScript to display the image.
Interactivity with JavaScript.
The first thing to do is to get the input
element from the DOM tree and assigning it to a variable. We do that by typing this:
const uploadButton = document.querySelector('input[type="file"]')
Next, we add an EventListener to the input element, we listen for a change event because a change event signifies that a file has been added, followed by an arrow function with the event object.
uploadButton.addEventListener('change', (e)=>{
const currFiles = e.target.files
})
After that, we get the current files in the object
uploadButton.addEventListener('change', (e)=>{
const currFiles = e.target.files
})
We then check to see if the currFiles array is not empty, get the first file in the array, and append that to our image element. We use the getElementById method to get the image element with an id of file-preview.
After that, we add the image src to the src attribute of our image element and change the display of the image element from display:none
to display:block
.
uploadButton.addEventListener('change', (e)=>{
const currFiles = e.target.files
if(currFiles.length > 0){
let src = URL.createObjectURL(currFiles[0])
let imagePreview = document.getElementById('file-preview')
imagePreview.src = src
imagePreview.style.display = "block"
}
})
Your final JS code should look like this:
let uploadButton = document.querySelector('input[type="file"]')
uploadButton.addEventListener('change', (e)=>{
const currFiles = e.target.files
if(currFiles.length > 0){
let src = URL.createObjectURL(currFiles[0])
let imagePreview = document.getElementById('file-preview')
imagePreview.src = src
imagePreview.style.display = "block"
}
})
That is all for the code. The final output should look like this:
It's a small image but it does the work of letting the user know that they have selected an image.
Thanks for reading this.
One interesting thing you could try to implement is zoom on click. When the user clicks on the preview image, they are able to see the image pop up like a modal and with more clarity.
Recommended Resources:
Banner Image source: https://web.dev/learn/forms/fields/
Top comments (2)
"Interactivity with JavaScript" was really helpful..thanks
I want to try this, thanks for tutorial