Many forms and apps will include a submit button (or similar) that, when clicked, will become temporarily disabled while some action is taking place. During the wait time, a good UI practice is to insert an animated loading indicator. This can be done with pure CSS while the JavaScript is just used to enable/disable the button while also performing some asynchronous action in the process. This could also be used when a general form submission is done synchronously.
Here is the HTML for the button:
<button>SUBMIT FORM<span class="spinner"></span></button>
Code language: HTML, XML (xml)
Here is the CSS (commented):
/* This is the submit button styles */
button {
display: block;
margin: 0 auto;
padding: .6em .8em;
/* Font-size is the root value that determines size of spinner parts.
Change this to whatever you want and spinner elements will size to match. */
font-size: 20px;
font-weight: bold;
border-radius: .4em;
border: none;
overflow: hidden;
cursor: pointer;
position: relative;
transition: all 1s;
}
/* focus/disabled styles, you can change this for accessibility */
button:focus, button:disabled {
outline: none;
background: #aaa;
}
/* This is the space for the spinner to appear, applied to the button */
.spin {
padding-left: 2.5em;
display: block;
}
/* position of the spinner when it appears, you might have to change these values */
.spin .spinner {
left: -.6em;
top: .4em;
width: 2.5em;
display: block;
position: absolute;
}
/* spinner animation */
@keyframes spinner {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
/* The actual spinner element is a pseudo-element */
.spin .spinner::before {
content: "";
width: 1.5em; /* Size of the spinner */
height: 1.5em; /* Change as desired */
position: absolute;
top: 50%;
left: 50%;
border-radius: 50%;
border: solid .35em #999; /* Thickness/color of spinner track */
border-bottom-color: #555; /* Color of variant spinner piece */
animation: .8s linear infinite spinner; /* speed of spinner */
transform: translate(-50%, -50%);
will-change: transform;
}
/* optional, but it will affect the size if changed */
*, *::before, *::after {
box-sizing: border-box;
}
Code language: CSS (css)
Finally, the JavaScript:
let btn = document.querySelector('button');
btn.addEventListener('click', function () {
// form submission starts
// button is disabled
btn.classList.add('spin');
btn.disabled = true;
// This disables the whole form via the fieldset
btn.form.firstElementChild.disabled = true;
// this setTimeout call mimics some asyncronous action
// you would have something else here
window.setTimeout(function () {
// when asyncronous action is done, remove the spinner
// re-enable button/fieldset
btn.classList.remove('spin');
btn.disabled = false;
btn.form.firstElementChild.disabled = false;
}, 4000);
}, false);
Code language: JavaScript (javascript)
And here is a live demo:
Some things worth noting:
- The JavaScript uses a
setTimeout()
call to mimic what might happen using Ajax. This would be removed and replaced with whatever you code is doing during the button submission process. - The size of the spinner is based on the
font-size
set on the<button>
element using pixels. Adjust this and the spinner size and position will change automatically to match via em units. - The button also disables the parent fieldset. This is optional, but it’s an added feature. You can disable this by removing the appropriate commented line in the code.
- The HTML in the demo disables the form from being submitted. This is only for demo purposes.
Related: Ladda spinners by Hakim El Hattab