Latest Web UI Trend: Ajax Loading Bars
When loading content in via ajax web browsers do not usually give clear indication they are fetching additional content. This is typically found with endless scrolling sites, however the latest UI trend as seen on Medium and Youtube is a top animated loading bar when fetching additional content. Here is a screenshot of the medium example:
So how do they do it?
Well lets start with some basic HTML/CSS. When using developer tools in chrome on the page I noticed that they kept adding a class to the body tag:
app-loading
So I added the class manually and the loading bar showed up and stayed put.Still using developer tools I found the div that made up the loading bar down at the bottom of the html right above the closing script tags. here is the HTML that makes up the loading bar:
<div class="loading-bar"></div>
Super simple so far, now the CSS, by default the style applied is:
.loading-bar
{
position: fixed;
display: none;
top: 0;
left: 0;
right: 0;
height: 2px;
z-index: 800;
background: #60d778;
-webkit-transform: translateX(100%);
-moz-transform: translateX(100%);
-o-transform: translateX(100%);
transform: translateX(100%);
}
Notice the display: none; This hides the bar from view initially. When we continue to look through the CSS we see that app-loading class overrides this default display: none.
.app-loading .loading-bar
{
display: block;
-webkit-animation: shift-rightwards 1s ease-in-out infinite;
-moz-animation: shift-rightwards 1s ease-in-out infinite;
-ms-animation: shift-rightwards 1s ease-in-out infinite;
-o-animation: shift-rightwards 1s ease-in-out infinite;
animation: shift-rightwards 1s ease-in-out infinite;
-webkit-animation-delay: .4s;
-moz-animation-delay: .4s;
-o-animation-delay: .4s;
animation-delay: .4s;
}
So by adding the app-loading class to the body while loading content into the page, you effectively get an animated loading bar fixed to the top of the page.
Last code sample is the CSS3 animation we "hijacked" from Medium, they called it: shift-rightwards, so lets give that developer credit and keep the name in the demo.
@-webkit-keyframes shift-rightwards
{
0%
{
-webkit-transform:translateX(-100%);
-moz-transform:translateX(-100%);
-o-transform:translateX(-100%);
transform:translateX(-100%);
}
40%
{
-webkit-transform:translateX(0%);
-moz-transform:translateX(0%);
-o-transform:translateX(0%);
transform:translateX(0%);
}
60%
{
-webkit-transform:translateX(0%);
-moz-transform:translateX(0%);
-o-transform:translateX(0%);
transform:translateX(0%);
}
100%
{
-webkit-transform:translateX(100%);
-moz-transform:translateX(100%);
-o-transform:translateX(100%);
transform:translateX(100%);
}
}
@-moz-keyframes shift-rightwards
{
0%
{
-webkit-transform:translateX(-100%);
-moz-transform:translateX(-100%);
-o-transform:translateX(-100%);
transform:translateX(-100%);
}
40%
{
-webkit-transform:translateX(0%);
-moz-transform:translateX(0%);
-o-transform:translateX(0%);
transform:translateX(0%);
}
60%
{
-webkit-transform:translateX(0%);
-moz-transform:translateX(0%);
-o-transform:translateX(0%);
transform:translateX(0%);
}
100%
{
-webkit-transform:translateX(100%);
-moz-transform:translateX(100%);
-o-transform:translateX(100%);
transform:translateX(100%);
}
}
@-o-keyframes shift-rightwards
{
0%
{
-webkit-transform:translateX(-100%);
-moz-transform:translateX(-100%);
-o-transform:translateX(-100%);
transform:translateX(-100%);
}
40%
{
-webkit-transform:translateX(0%);
-moz-transform:translateX(0%);
-o-transform:translateX(0%);
transform:translateX(0%);
}
60%
{
-webkit-transform:translateX(0%);
-moz-transform:translateX(0%);
-o-transform:translateX(0%);
transform:translateX(0%);
}
100%
{
-webkit-transform:translateX(100%);
-moz-transform:translateX(100%);
-o-transform:translateX(100%);
transform:translateX(100%);
}
}
@keyframes shift-rightwards
{
0%
{
-webkit-transform:translateX(-100%);
-moz-transform:translateX(-100%);
-o-transform:translateX(-100%);
transform:translateX(-100%);
}
40%
{
-webkit-transform:translateX(0%);
-moz-transform:translateX(0%);
-o-transform:translateX(0%);
transform:translateX(0%);
}
60%
{
-webkit-transform:translateX(0%);
-moz-transform:translateX(0%);
-o-transform:translateX(0%);
transform:translateX(0%);
}
100%
{
-webkit-transform:translateX(100%);
-moz-transform:translateX(100%);
-o-transform:translateX(100%);
transform:translateX(100%);
}
}
Easy enough, so how can we apply this to our own websites?
- Just before making an ajax request add the class app-loading to the < body / > tag.
- After the ajax has finished (make sure also to catch on timeout or error) remove the class from the body tag.
Full Working Example using jQuery:
http://codepen.io/artfulhacker/pen/hikJd
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Loading Bar Sample</title>
<style>
.app-loading .loading-bar
{
display: block;
-webkit-animation: shift-rightwards 1s ease-in-out infinite;
-moz-animation: shift-rightwards 1s ease-in-out infinite;
-ms-animation: shift-rightwards 1s ease-in-out infinite;
-o-animation: shift-rightwards 1s ease-in-out infinite;
animation: shift-rightwards 1s ease-in-out infinite;
-webkit-animation-delay: .4s;
-moz-animation-delay: .4s;
-o-animation-delay: .4s;
animation-delay: .4s;
}
.loading-bar
{
position: fixed;
display: none;
top: 0;
left: 0;
right: 0;
height: 2px;
z-index: 800;
background: #60d778;
-webkit-transform: translateX(100%);
-moz-transform: translateX(100%);
-o-transform: translateX(100%);
transform: translateX(100%);
}
@-webkit-keyframes shift-rightwards
{
0%
{
-webkit-transform:translateX(-100%);
-moz-transform:translateX(-100%);
-o-transform:translateX(-100%);
transform:translateX(-100%);
}
40%
{
-webkit-transform:translateX(0%);
-moz-transform:translateX(0%);
-o-transform:translateX(0%);
transform:translateX(0%);
}
60%
{
-webkit-transform:translateX(0%);
-moz-transform:translateX(0%);
-o-transform:translateX(0%);
transform:translateX(0%);
}
100%
{
-webkit-transform:translateX(100%);
-moz-transform:translateX(100%);
-o-transform:translateX(100%);
transform:translateX(100%);
}
}
@-moz-keyframes shift-rightwards
{
0%
{
-webkit-transform:translateX(-100%);
-moz-transform:translateX(-100%);
-o-transform:translateX(-100%);
transform:translateX(-100%);
}
40%
{
-webkit-transform:translateX(0%);
-moz-transform:translateX(0%);
-o-transform:translateX(0%);
transform:translateX(0%);
}
60%
{
-webkit-transform:translateX(0%);
-moz-transform:translateX(0%);
-o-transform:translateX(0%);
transform:translateX(0%);
}
100%
{
-webkit-transform:translateX(100%);
-moz-transform:translateX(100%);
-o-transform:translateX(100%);
transform:translateX(100%);
}
}
@-o-keyframes shift-rightwards
{
0%
{
-webkit-transform:translateX(-100%);
-moz-transform:translateX(-100%);
-o-transform:translateX(-100%);
transform:translateX(-100%);
}
40%
{
-webkit-transform:translateX(0%);
-moz-transform:translateX(0%);
-o-transform:translateX(0%);
transform:translateX(0%);
}
60%
{
-webkit-transform:translateX(0%);
-moz-transform:translateX(0%);
-o-transform:translateX(0%);
transform:translateX(0%);
}
100%
{
-webkit-transform:translateX(100%);
-moz-transform:translateX(100%);
-o-transform:translateX(100%);
transform:translateX(100%);
}
}
@keyframes shift-rightwards
{
0%
{
-webkit-transform:translateX(-100%);
-moz-transform:translateX(-100%);
-o-transform:translateX(-100%);
transform:translateX(-100%);
}
40%
{
-webkit-transform:translateX(0%);
-moz-transform:translateX(0%);
-o-transform:translateX(0%);
transform:translateX(0%);
}
60%
{
-webkit-transform:translateX(0%);
-moz-transform:translateX(0%);
-o-transform:translateX(0%);
transform:translateX(0%);
}
100%
{
-webkit-transform:translateX(100%);
-moz-transform:translateX(100%);
-o-transform:translateX(100%);
transform:translateX(100%);
}
}
</style>
</head>
<body>
<div>random content here</div>
<div class="loading-bar"></div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
$(function()
{
$("body").addClass("app-loading");
$.get("some-test-page.html", function(data)
{
$("body").removeClass("app-loading");
});
})
</script>
</body>
</html>
Written by Mike Olsen
Related protips
6 Responses
So, the animation time isn't related to content downloaded asynchronously?
It just says "we are doing something a bit long"?
I may be better to use an infinite wheel, no?
but nice explanation of the feature!
Thanks for the comment. Thats why I referred to it as a "loading bar" vs "progress bar" at least in my mind they are different but I should have made that more clear. It appears that the youtube one might actually be a progress bar.. let me see if I can write a follow up that would give a true progress.
Okay, get the difference. This visual effect is definitely associated to finite progress to me.
It quite hard to represent undetermined (or hard to determine precisely) task time, but this one is certainly an acceptable answer! Not the best to me, that's all ;)
AJAX loaders have no end! Youtube got the same one too
I was going to post a follow up where the method could take in a 1-100 value and draw it accordingly. This would give you settable progress, if you could somehow calculate it. For long running processes the data could be fed into the client with separate ajax calls.
You can turn it into an actual progress bar if you use the new progress event in XHR2! http://stackoverflow.com/questions/16690740/how-to-show-loading-status-in-percentage-for-ajax-response#answer-16716261
In the CSS you would have the bar's width initially be 0% and then change it using javascript to show the actual progress.