/feb 24, 2015

Building a scroll progress bar with JavaScript and jQuery

By Tyler Waneka

I was reading an article on Pitchfork.com the other day and I noticed this nifty little progress bar that let me know how much of the article I had left to go. I love little details such as this one. Very simple but a really nice user experience; so I decided to recreate it.

My first step was to layout some basic HTML and CSS. The HTML includes a .header-container div with a header and the progress bar nested within.

The .scroll-progress-container div will be where the progress bar lives, and we'll update the width of the .scroll-progress div as we scroll down the page.

There's some filler text (Monty Python edition) to make the page nice and long to show off the scrolling. I've omitted most of the text for the purpose of this post, but if you're following along at home go ahead and put enough text in there to make the document scrollable.

I've also included jQuery to make things easier in a bit.

<!doctype html>
<head>
<title></title>
<link rel="stylesheet" type="text/css" href="main.css">
<script src="https://code.jquery.com/jquery-1.11.2.min.js"></script>
<script type="text/javascript" src="main.js"></script>
</head>
<body>
<div class="header-container">
<div class="header">
<h1>Progress Scroll Bar Example</h1>
</div>
<div class="scroll-progress-container">
<div class="scroll-progress"></div>
</div>
</div>

<p class="filler-text">
The Lady of the Lake, her arm clad in the purest shimmering samite, held aloft Excalibur from the bosom of the water...
</p>

</body>
</html>

Now for some CSS. We'll set the .header-container to be position: fixed; so it stays at the top of the window as we scroll.

The .scroll-progress-container we'll give a height of 5px and set it be 55px from the top (5px less than the height of .header-container).

.scroll-progress will initially have a width of 0px, which we'll be updating as we scroll using Javascript.

.header-container {
  width: 100%;
  height: 60px;
  background-color: white;
  position: fixed;
  z-index: 10;
  top: 0;
  left: 0;
}

.header {
  padding-left: 10px;
}

h1 {
  margin-top: 15px;
}

.scroll-progress-container {
  width: 100%;
  height: 5px;
  background-color: white;
  top: 55px;
  position: absolute;
}

.scroll-progress {
  width: 0px;
  height: 5px;
  background-color: purple;
}

.filler-text {
  width: 60%;
  margin-top: 80px;
  margin-left: 50px;
  position: absolute;
}

Now for the Javascript. We've got a couple problems to solve.

  • What is the total height of the document?
  • What is the position of the window in relation to the document height at any given time?
  • How can we update the width of the progess bar as we scroll?

jQuery has some really nice methods for all this, which is why we've included it. For starters, finding the total height of the document is as easy as

$(document).height()

Nice! So now we'll need to find the position of the window in relation to the document. This is also made very easy with jQuery

$(window).scrollTop()

.scrollTop() will return the vertical position of the scroll bar for the first element in the set of matched elements, in our case $(window). Also nice!

Like most jQuery methods, these are available as getters and setters. We're using these two as getters, but for our next step we'll use a setter.

Let's look at jQuery's width method.

$('.scroll-progress').width(value)

That will set the width of the .scroll-progress div to the given value.

Using jQuery's .scroll event handler, we can update the width of the progress bar dynamically as the user scrolls. Let's see where we're at so far.

$(document).ready(function() {
  var docHeight = $(document).height(),
  scrollPercent;

  $(window).scroll(function() {
    scrollPercent = ($(window).scrollTop() / docHeight) * 100;

    $('.scroll-progress').width(scrollPercent + '%');
    });
    });

Looking good! Everytime the .scroll event handler fires, we update scrollPercent with the position of the top of the $(window) divided by the total document height. When multiplied by 100, that'll give us the percentage of the document that we've scroll thus far. Since the .width(value) setter accepts a numerical value, we'll append the '%' sign to signal it's a percentage.

One little bug to fix though. You'll notice that the progress bar stops short of the full width, even when we've scrolled all the way to the bottom. This is because .scrollTop() returns the vertical postion for the top of the matched element. So when we've scrolled all the way to the bottom of the document, $(window).scrollTop() is returning the top of the window. The fix is simple.

    $(document).ready(function() {
      var docHeight = $(document).height(),
      windowHeight = $(window).height(),
      scrollPercent;

      $(window).scroll(function() {
        scrollPercent = $(window).scrollTop() / (docHeight - windowHeight) * 100;

        $('.scroll-progress').width(scrollPercent + '%');
        });
        });

And there we have it. A quick and relatively straight forward implementation of a scrolling progress bar.

There are several more improvements one could make, such as adjusting for the presence of a footer or adjusting if a user changes the browser height, but this should be a nice building block.

Hope you enjoy!

Related Posts

By Tyler Waneka