In this post, we’ll see how to style an input element used to add images:
Into one that adds images and generates a preview with a “+” button:
(Clicking on the “submit” button will print a list of the added image files)
We’ll obtain this result by using a second input as a helper, storing the added image files in an array, and setting the input files before submitting.
Markup
Our markup will consist of a form with two input elements.
<form class="custom__form"> <p>Add image</p> <div class="custom__image-container"> <label id="add-img-label" for="add-single-img">+</label> <input type="file" id="add-single-img" accept="image/jpeg" /> </div> <input type="file" id="image-input" name="photos" accept="image/jpeg" multiple /> <br /> <div class="form__controls"><button type="submit">Submit</button></div> </form>
The first input element add-single-img
will be used to add the image files one by one. However, the input image-input
is the one we really care about when submitting the form.
One important aspect is that the +
label element is associated with the helper input via the for
attribute. This way, when we click on it, the input is triggered.
Styling
Both input elements will be hidden because it’s difficult to modify and style them directly.
.custom__form input { opacity: 0; height: 0; }
The “+” button used to select images is placed inside a container. As we select images, a preview of them will be added to this same container.
.custom__image-container { display: flex; flex-wrap: wrap; gap: 0.5rem; }
Since the helper input element is hidden, we interact with it via its label (important to set the for
attribute accordingly). We style it to look like a square button.
.custom__image-container label { display: flex; justify-content: center; align-items: center; font-size: 150%; cursor: pointer; width: 100px; height: 100px; border: solid 1px black; border-radius: 5px; object-fit: cover; }
Similar rules are applied to img
elements that will be added to the container as we select new image files.
.custom__image-container img { width: 100px; height: 100px; border: solid 1px black; border-radius: 5px; object-fit: cover; }
Scripts
We start by defining the following global variables.
const imgInputHelper = document.getElementById("add-single-img"); const imgInputHelperLabel = document.getElementById("add-img-label"); const imgContainer = document.querySelector(".custom__image-container"); const imgFiles = [];
The first three are there only for convenience. The global imgFiles
will store the image files that the user selects.
Adding image previews
Every time we select an image via the +
button, a preview of it will be generated. We’ll trigger this effect via a “change” event listener on the helper input element.
const addImgHandler = () => { const file = imgInputHelper.files[0]; if (!file) return; // Generate img preview const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = () => { const newImg = document.createElement("img"); newImg.src = reader.result; imgContainer.insertBefore(newImg, imgInputHelperLabel); }; // Store img file imgFiles.push(file); // Reset image input imgInputHelper.value = ""; return; }; imgInputHelper.addEventListener("change", addImgHandler);
To generate the image preview, we used a FileReader. A more direct approach would use the URL.createObjectURL
method, as can be seen in this example.
Creating a list of files
We stored the added images in the imgFiles
array. Input elements store files in FileList
objects. There is no “FileList” constructor. We will create it indirectly via a DataTransfer
object.
const getImgFileList = (imgFiles) => { const imgFilesHelper = new DataTransfer(); imgFiles.forEach((imgFile) => imgFilesHelper.items.add(imgFile)); return imgFilesHelper.files; };
The function above takes the array of Files imgFiles
that we used previously to store the image files. It returns a FileList
generated by using the DataTransfer
object.
Credit for this approach goes to this StackOverflow answer.
Submitting the list of files
Before the actual form submission, we will take the files stored in imgFiles
, create a “FileList” and assign it to the input element (not the helper).
const customFormSubmitHandler = (ev) => { ev.preventDefault(); const firstImgInput = document.getElementById("image-input"); firstImgInput.files = getImgFileList(imgFiles); // submit form to server, etc }; document .querySelector(".custom__form") .addEventListener("submit", customFormSubmitHandler);
As a result, we’ll have our original input element image-input
with a FileList as if we had selected them directly using the native element.
Access the source code here.
THANK YOUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
But wait how do u make it so that it doesn’t restart/repeat a new input field, lets say you only need 5?
thank u