How to Upload Files With Vanilla JavaScript and Add a Loading Animation

File upload is very ubiquitous to any web application and when it comes to uploading files and resources over the internet (on a browser), things can be somewhat stressful. Fortunately, with HTML 5, input elements which usually come with form control to allow users to modify data can become so handy in simplifying uploading resources.

In this article, we would have a closer look at how to handle file uploads using vanilla JavaScript. The aim is to teach you how to build a file upload component without the need for an external library and also learn some core concepts in JavaScript. You will also learn how to show the progress status of an upload as it happens.

Source Code: As usual, you can tinker with the source code hosted on GitHub repo for the project.

Project Setup

First and foremost, in your preferred directory, create a new folder for the project:

$ mkdir file-upload-progress-bar-javascript
Enter fullscreen mode Exit fullscreen mode

After doing so, let us now create index.html, main.css, and app.js files where we will write all the markup for our project.

$ touch index.html && touch main.css && touch app.js
Enter fullscreen mode Exit fullscreen mode

Now we can begin to build the structure for the file upload by creating a basic HTML template with <head> and <body> tags:

<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>File Upload with Progress Bar using JavaScript</title> </head> <body></body>
</html>
Enter fullscreen mode Exit fullscreen mode

Next, we add base styles for the project in main.css:

* { margin: 0; padding: 0; box-sizing: border-box;
}
Enter fullscreen mode Exit fullscreen mode

To enhance the look of our applications, we will make use of the icons from the font awesome library which we can add to our project through a kit code that can be created at the official font awesome library website.

Now, index.html is updated, and the main.css file is linked:

<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> https://kit.fontawesome.com/355573397a.js <link rel="stylesheet" href="main.css"> <title>File Upload with Progress Bar using JavaScript</title> </head> <body></body>
</html>
Enter fullscreen mode Exit fullscreen mode

We continue by building the structure for the file uploader:

<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> https://kit.fontawesome.com/355573397a.js <link rel="stylesheet" href="main.css" /> <title>File Upload with Progress Bar using JavaScript</title> </head> <body> <div class="file-upload__wrapper"> <header>File Uploader JavaScript with Progress</header> <div class="form-parent"> <form action="#" class="file-upload__form"> <input class="file-input" type="file" name="file" hidden /> <i class="fas fa-cloud-upload-alt"></i> <p>Browse File to Upload</p> </form> <div> <section class="progress-container"></section> <section class="uploaded-container"></section> </div> </div> </div> http://app.js </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Then, copy/paste the following codes to update main.css:

* { margin: 0; padding: 0; box-sizing: border-box;
}
body { min-height: 100vh; background: #cb67e9; display: flex; align-items: center; justify-content: center; font-family: Arial, Helvetica, sans-serif;
}
::selection { color: white; background: #cb67e9;
}
.file-upload__wrapper { width: 640px; background: #fff; border-radius: 5px; padding: 35px; box-shadow: 6px 6px 12px rgba(0, 0, 0, 0.05);
}
.file-upload__wrapper header { color: #cb67e9; font-size: 2rem; text-align: center; margin-bottom: 20px;
}
.form-parent { display: flex; align-items: center; gap: 30px; justify-content: center;
}
.file-upload__wrapper form.file-upload__form { height: 150px; border: 2px dashed #cb67e9; cursor: pointer; margin: 30px 0; display: flex; align-items: center; flex-direction: column; justify-content: center; border-radius: 6px; padding: 10px;
}
form.file-upload__form :where(i, p) { color: #cb67e9;
}
form.file-upload__form i { font-size: 50px;
}
form.file-upload__form p { font-size: 1rem; margin-top: 15px;
}
section .row { background: #e9f0ff; margin-bottom: 10px; list-style: none; padding: 15px 12px; display: flex; align-items: center; justify-content: space-between; border-radius: 6px;
}
section .row i { font-size: 2rem; color: #cb67e9;
}
section .details span { font-size: 1rem;
}
.progress-container .row .content-wrapper { margin-left: 15px; width: 100%;
}
.progress-container .details { display: flex; justify-content: space-between; align-items: center; margin-bottom: 7px;
}
.progress-container .content .progress-bar-wrapper { height: 10px; width: 100%; margin-bottom: 5px; background: #fff; border-radius: 30px;
}
.content .progress-bar .progress-wrapper { height: 100%; background: #cb67e9; width: 0%; border-radius: 6px;
}
.uploaded-container { overflow-y: scroll; max-height: 230px;
}
.uploaded-container.onprogress { max-height: 160px;
}
.uploaded-container .row .content-wrapper { display: flex; align-items: center;
}
.uploaded-container .row .details-wrapper { display: flex; flex-direction: column; margin-left: 15px;
}
.uploaded-container .row .details-wrapper .name span { color: green; font-size: 10px;
}
.uploaded-container .row .details-wrapper .file-size { color: #404040; font-size: 11px;
}
Enter fullscreen mode Exit fullscreen mode

Now, the component should be looking better on the browser:

Adding Upload Functionality

To add the required functionality for uploading in our project, we now make use of the app.js file, where we write JavaScript codes that give life to our project.

Copy/paste the following into app.js:

const uploadForm = document.querySelector(".file-upload__form");
const myInput = document.querySelector(".file-input");
const progressContainer = document.querySelector(".progress-container");
const uploadedContainer = document.querySelector(".uploaded-container");
uploadForm.addEventListener("click", () => { myInput.click();
});
myInput.onchange = ({ target }) => { let file = target.files[0]; if (file) { let fileName = file.name; if (fileName.length >= 12) { let splitName = fileName.split("."); fileName = splitName[0].substring(0, 13) + "... ." + splitName[1]; } uploadFile(fileName); }
};
function uploadFile(name) { let xhrRequest = new XMLHttpRequest(); const endpoint = "uploadFile.php"; xhrRequest.open("POST", endpoint); xhrRequest.upload.addEventListener("progress", ({ loaded, total }) => { let fileLoaded = Math.floor((loaded / total) * 100); let fileTotal = Math.floor(total / 1000); let fileSize; fileTotal < 1024 ? (fileSize = fileTotal + " KB") : (fileSize = (loaded / (1024 * 1024)).toFixed(2) + " MB"); let progressMarkup = `<li class="row"> <i class="fas fa-file-alt"></i> <div class="content-wrapper"> <div class="details-wrapper"> <span class="name">${name} | <span>Uploading</span></span> <span class="percent">${fileLoaded}%</span> </div> <div class="progress-bar-wrapper"> <div class="progress-wrapper" style="width: ${fileLoaded}%"></div> </div> </div> </li>`; uploadedContainer.classList.add("onprogress"); progressContainer.innerHTML = progressMarkup; if (loaded == total) { progressContainer.innerHTML = ""; let uploadedMarkup = `<li class="row"> <div class="content-wrapper upload"> <i class="fas fa-file-alt"></i> <div class="details-wrapper"> <span class="name">${name} | <span>Uploaded</span></span> <span class="file-size">${fileSize}</span> </div> </div> </li>`; uploadedContainer.classList.remove("onprogress"); uploadedContainer.insertAdjacentHTML("afterbegin", uploadedMarkup); } }); let data = new FormData(uploadForm); xhrRequest.send(data);
}
Enter fullscreen mode Exit fullscreen mode

What we have done is to be able to read a file that has been selected from using the file input element, and create a new list of files on the DOM. When the file is being uploaded, the progress level is shown, and when the file is completed uploaded, the progress status changes to uploaded.

Then, we also add an uploadFile.php to our project to mock an endpoint for sending files. The reason for this is to simulate asynchronosity in our project so that we get the progress loading effect.

<?php $file_name = $_FILES['file']['name']; $tmp_name = $_FILES['file']['tmp_name']; $file_up_name = time().$file_name; move_uploaded_file($tmp_name, "files/".$file_up_name);
?>
Enter fullscreen mode Exit fullscreen mode

Conclusion

You have been awesome to get through to this point of this article.

In this tutorial, you have learned how to build a file upload component and add a progress bar to it. This can be useful when you build websites and want your users to feel included and get a sense of how slow or fast uploading a file is going. Feel free to re-use this project whenever you wish to.

If you get stuck while following along with this tutorial, I suggest you upload your project on GitHub for help from other developers or you can also send a dm to me too, I’ll be happy to help.

Here is a link to the GitHub repo for the project.

Relevant Resources


Discover more from Coursity

Subscribe to get the latest posts sent to your email.

Leave a Comment

Your email address will not be published. Required fields are marked *

Discover more from Coursity

Subscribe now to keep reading and get access to the full archive.

Continue reading

Scroll to Top