Progress indicators in user interface design are often overlooked, but they have a significant impact on the user experience. They inform and engage users during tasks such as file downloads, content uploads, or process completion. Designing an effective and captivating progress indicator requires careful consideration.
In this Educative Answer, we will explore how to create a progress indicator in a Svelte application that specifically focuses on file uploads. As the upload progresses, we’ll display a progress indicator to show the user how far along they are in the upload process.
Note: We won’t go into the details of how to develop a Svelte application from scratch and are using the default template to display our progress indicator module. However, if you are interested in learning more about Svelte, you can find more information on the topic in our Building Reactive Apps with Svelte and Tailwind course.
The following screenshot shows what our application would look like:
We will explore the implementation details of the progress bar in a Svelte application.
We’ll examine the HTML structure and the JavaScript logic that work together to create a dynamic and visually appealing progress indicator.
By understanding these components, we’ll be able to integrate a progress bar into our own Svelte applications and enhance the user experience during file uploads or other relevant tasks.
The HTML structure for the progress bar in the Svelte application consists of the following elements:
<div id="msg-cont">
element in our code, is responsible for displaying the completion message when the progress reaches . It wraps the completion message element <h3 id="completion-msg">
.<div id="msg-cont">{#if barWidth === 100}<h3 id="completion-msg" in:fly={{x: -300}} out:fly={{x: 300}}>The file has been uploaded!</h3>{/if}</div>
<div id="progress">
element acts as the container for the progress bar and percentage display. It controls the visibility of these elements based on the isActive
variable.<div id="progress">{#if isActive}<span>{barWidth}%</span><div id="myBar" style="width: {barWidth}%"></div>{/if}</div>
<form>
element encapsulates the file upload functionality. It includes an <input type="file">
element for selecting the file, along with an “Upload File” button and a “Cancel” button. The makeProgress
and resetBar
functions handle the form submission and cancellation, respectively.<form on:submit|preventDefault={makeProgress}><input type="file" id="myFile" name="filename" bind:this={chooseFileBtn}><button disabled={isActive}>Upload File</button><button on:click|stopPropagation|preventDefault={resetBar}>Cancel</button></form>
This HTML structure defines the layout and functionality of the progress bar in the Svelte application.
The JavaScript structure for the progress bar in the Svelte application consists of the following elements:
Variables: The following code defines several variables using the let
keyword:
barWidth
: Tracks the current progress of the upload, initially set to 0
.
progress
: Stores the interval reference for updating the progress bar.
isActive
: Indicates whether the progress bar is active or not.
chooseFileBtn
: Stores a reference to the file input element using the bind:this
directive.
let barWidth = 0;let progress; // for setIntervallet isActive = false;let chooseFileBtn; // use with bind:this and the input type="file"
Reactive statement: The code utilizes a reactive statement to observe changes in the barWidth
variable. When barWidth
reaches , the reactive statement triggers two actions:
stopProgress()
: Calls the stopProgress
function to clear the interval and reset the input value after a delay of 2500ms.
resetBar()
: Calls the resetBar
function to reset the progress bar to its initial state.
$: if (barWidth === 100) {stopProgress();setTimeout(resetBar, 2500);}
Functions
stopProgress()
: This function stops the progress by clearing the interval and resetting the input value. It is invoked when the upload is completed or canceled.
resetBar()
: This function resets the progress bar by setting barWidth
to 0
and calling stopProgress()
.
addColor()
: This function increments the barWidth
variable by 1
, simulating progress. It is called repeatedly using setInterval
during active progress.
randomTime()
: This function generates a random time value between 0
and 60
, used as the interval duration for updating the progress bar.
makeProgress()
: This function is triggered when the upload is initiated. It first checks if a file has been chosen using chooseFileBtn.value
. If a file is chosen, it sets isActive
to true
and calls setInterval
with addColor
and randomTime
to start updating the progress bar.
const stopProgress = () => {clearInterval(progress);isActive = false;chooseFileBtn.value = "";}const resetBar = () => {stopProgress();barWidth = 0;}const addColor = () => barWidth += 1;const randomTime = () => Math.floor(Math.random() * 60)const makeProgress = () => {if (!chooseFileBtn.value) {alert("Choose a file")} else {isActive = true;progress = setInterval(addColor, randomTime());}}
It provides the necessary functions to start, stop, and reset the progress and simulate progress updates using intervals.
We can run our application by pressing the “Run” button in the widget below:
Note: There is some supplementary code included alongside the HTML and JS that we previously discussed. However, it is important to note that the code responsible for creating the progress bar remains largely unchanged, just as we discussed.
<script> import { fade, fly } from 'svelte/transition'; let barWidth = 0; let progress; // for setInterval let isActive = false; let isLoading = true; // added variable let chooseFileBtn; // use with bind:this and the input type="file" let showCompletionMsg = false; // added variable let completionMsgVisible = false; const stopProgress = () => { clearInterval(progress); isActive = false; chooseFileBtn.value = ""; }; const resetBar = () => { stopProgress(); barWidth = 0; completionMsgVisible = false; }; const addColor = () => { if (barWidth < 100) { barWidth += 1; } else { stopProgress(); showCompletionMsg = true; setTimeout(() => { showCompletionMsg = false; resetBar(); }, 3000); } }; const randomTime = () => Math.floor(Math.random() * 60); const makeProgress = () => { if (isLoading) return; if (!chooseFileBtn.value) { alert("Choose a file"); } else { isActive = true; progress = setInterval(addColor, randomTime()); } }; setTimeout(() => { isLoading = false; }, 10); </script> <style> #progress { width: 100%; height: 50px; position: relative; margin-top: 5%; display: flex; align-items: center; } #myBar { height: 40px; background-color: #9ee86a; border: 1px solid #333; transition: width 0.3s; position: relative; overflow: hidden; } #myBar span { font-size: 1.2rem; color: white; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); z-index: 1; } form { margin-top: 1%; width: 100%; text-align: center; } button { cursor: pointer; width: 100px; padding: 9.5px 0; } button:active { background-color: hsl(20, 58%, 55%); color: white; } @keyframes blink { to { opacity: 0; } } #loading-text { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); } #msg-cont { height: 50px; padding: 10px; } h3#completion-msg { margin: 0 auto; text-align: center; animation: blink 1.5s ease-in-out 4; } </style> {#if isLoading} <!-- Show loading text while isLoading is true --> <div id="loading-text">Loading...</div> {:else} <!-- Show the application content when isLoading is false --> <div> <div id="msg-cont"> {#if showCompletionMsg} <h3 id="completion-msg" in:fly={{ x: -300 }} out:fly={{ x: 300 }}> The file has been uploaded! The progress indicator works! </h3> {/if} </div> <div id="progress"> {#if isActive} <div id="myBar" style="width: {barWidth}%"> <span>{barWidth}%</span> </div> {/if} </div> <form on:submit|preventDefault={makeProgress}> <input type="file" id="myFile" name="filename" bind:this={chooseFileBtn}> <button disabled={isActive}>Upload File</button> <button on:click|stopPropagation|preventDefault={resetBar}>Cancel</button> </form> </div> {/if}