Show a loading bar when the page is loading

Loading...

I was working on a web app recently and I noticed that every time I clicked on a link, I couldn't tell if the next page was loading or not. On desktop browsers it was obvious enough with the browser queue, but not so much on mobile. So I decided to add a loading bar to let the user know.

Animation CSS:

<style>
@keyframes page-load {
    from {
        width:0;
    }
    to {
        width:100%;
    }
}
.page-loading::before {
    content:" ";
    display:block;
    position:fixed;
    z-index:10;
    height:2px;
    width:100%;
    top:0;
    left:0;
    background-color:#06D;
    animation: page-load ease-out 2s;
}
</style>

Creating the bar in CSS is easy enough. The bar just has to grow from 0 to 100%. But figuring out the logic on when to show the bar was a challenge. So here are the different solutions I tried:

1. On anchor tag click

My first attempt was to add an event handler on every link on the page. Clicking on them would add a class on the body and would animate a loading bar similar to the one you see below.

sample page

Javascript Code:

var links = document.getElementsByTagName("a"),
i = 0, l = links.length,
body = document.body;

for(;i<l;i++) {
    links[i].addEventListener("click",function(){
        body.className = "page-loading";
    },false);
}

This worked perfectly at first but then I realized that I had some anchor tags on the page the trigger javascript or an ajax request. This meant that the animation was triggered all the time and the loading bar is left on the top.

2. On click animate then remove.

I decided that it was fine to run the animation on every link click as long as the animation is removed right after. So I added a timer to remove the animation 3 seconds later.

for(;i<l;i++) {
    links[i].addEventListener("click",function(){
        body.className = "page-loading";
        setTimeout(function(){
            body.className = "";
        },3000);
    },false);
}

This solution worked for my case, but I couldn't help but think that it is messy. It looks more like a hack then a permanent solution. If the next page takes more than 3 seconds to load, the animation is removed and it confuses the user. Also, when the bar fills the length of the page, the user thinks the page is done loading, while that's not the case. It's just an animation. So I decided to change from a growing loading bar to a full bar in flickering colors.

<style>
@keyframes page-load {
    from {
        background-color: #ffc422;
    }
    to {
        background-color: #c0392b;
    }
}
</style>
sample page

This looks much nicer, but we still have to remove the bar 3 seconds later, which is messy. So let's explore my final solution.

Final Solution: onbeforeunload Event

This is the most elegant solution. Adding event handler to every single button is too much overhead. Instead here, we add a single event onbeforeunload to the page and when a user clicks on a link we can add our loading bar to the page.

window.addEventListener("beforeunload",function(e){
    document.body.className = "page-loading";
},false);

This will not be triggered on ajax calls or on button clicks that don't cause the whole page to unload. So we also don't need to add a timeout to remove the bar at some point. The bar will be removed when the next page is ready to load.

For my case I placed the CSS in a media query that only displays on mobile browsers but I think it can be a convenient feature to let the user know that the page is loading.

Final CSS Code:

@keyframes page-load {
    from {
        background-color: #ffc422;
    }
    to {
        background-color: #c0392b;
    }
}
.page-loading::before {
    content:" ";
    display:block;
    position:fixed;
    z-index:10;
    height:2px;
    width:100%;
    top:0;
    left:0;
    background-color:#06D;
    animation: page-load infinite ease-out 2s;
    box-shadow:0 2px 2px rgba(0,0,0,.2);
}

Final JavaScript:

window.addEventListener("beforeunload",function(e){
    document.body.className = "page-loading";
},false);

I hope you find it useful as well.


Comments(10)

James :

This was a FANTASTIC solution to my issue. thanks for the detailed post. I ended up having to change the script to make it work on my site, as I make use of custom body classes, and this would replace them, making ugly css changes while loading. Instead of replacing the classes, I added one. document.body.classList.add("page-loading");

See it in action https://3aenglish.com

Ibrahim :

Hi James, I'm glad it worked out well for your use case. Looks great ;)

mubashir :

that great please tell me how to integrate in wordpress please email the solution at minhajahmed214@gmail.com

James :

Hi Ibrahim, I switched it temporarily to simply fade out and then used keyframes to fade in. Kind of neat, but I want to incorporate some sort of animation like your coloured bar. How can we show it "onload" as well? It seems to ignore a second event listener. Any way to flow something between "onunload" and "onload" to make it feel seamless and continue the animation through the whole process.

PS, I didn't get a comment notification from your reply, but I'll check back for your response!

Ibrahim :

James, the animation that I used is from the code snippet in the example. All it is doing is fading from one color to the other, creating rainbows in between.

Instead of from and to in the animation, you can use percentages to define as many colors as you want.

To make it appear on page load, you have to have a default style (ex: a class on the body) that starts the animation. And when the page is loaded, you remove the class from the body.

I hope this helps.

ducphuli :

Thanks Ibrahim, :)

Ibrahim :

@ducphuli you are welcome. Glad I could help you

saroj kumar dash :

<style>
    @keyframes page-load {
        from {
            background-color: #ffc422;
        }

        to {
            background-color: #c0392b;
        }
    }

    .page-loading::before {
        content: " ";
        display: block;
        position: fixed;
        z-index: 10;
        height: 2px;
        width: 100%;
        top: 0;
        left: 0;
        background-color: #06D;
        animation: page-load infinite ease-out 2s;
        box-shadow: 0 2px 2px rgba(0,0,0,.2);
    }
</style>
<script>
window.addEventListener("beforeunload", function (e) {
    document.body.className = "page-loading";
}, false);
</script>

This line of code i put in the head tag but it is not working

Ibrahim :

I just tested it out on this same page and it worked. Maybe your next page is loading too quickly and you don't see it?

Lamine :

Hello! This doesn't work on any Safari browser, including iOS browsers or any. Is there any way I can get it to work on Safari?

Let's hear your thoughts

For my eyes only