How to show Image Preview from Input element.
Tags: webdev, javascript, css, html
Published: 2022-02-12 18:34:34

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**](https://web.dev/) by Google developers has a wonderful module on forms. You can access it by clicking [**here**](https://web.dev/learn/forms/)

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:

```HTML
<input type="file></input>
```

![Input type file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/94a6pnsyhbydlz7fkoif.png)

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**](https://jsfiddle.net/f1bc7vsy/35/)

If you do want to code along from scratch, let's get started.

This project is going to be divided into three sections:

1. The Markup
2. The Styling
3. Interactivity with JavaScript

###**The Markup**
The Markup is the HTML we have to work with. The required elements we need are:

```html
<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:

![basic input form control](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qx575o4c6r8pup6crg0z.png)

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

```CSS
body{
  height: 100vh;
}
```

Next, we style our container.
```CSS
.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:
![basic form control styling](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xaoctrnu06f7i2ul0bgl.png)

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

```CSS
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](https://developer.mozilla.org/en-US/docs/Web/CSS/:active):

>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:

![button styled with input element](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ajp7p7xhzgtyygqzqlhs.png)

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:

```CSS
input#file {
  width: 0;
  height: 0;
}
```
which gives us:
![button](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fk2vmmnqg4fugqq1ky62.png)

Here it is in motion:

![button click animation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s7trlk35jebjn6dvwoyj.gif)

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:

```CSS
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:

```javascript
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.

```javascript
uploadButton.addEventListener('change', (e)=>{
   const currFiles = e.target.files
})
```
After that, we get the current files in the object

```javascript
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`. 

```javascript
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:

```javascript
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:


![button-upload image preview](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iggndwcr46xcrpzfxqun.gif)

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:

1. [MDN article on input type file](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file)

2. [MDN article on createObjectURL](https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL)

3. [MDN article on the DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model)
 
Banner Image source: https://web.dev/learn/forms/fields/