Creating A Reading Progress Bar In JavaScript

Adding a progress bar to the top of the page is a simple way of showing the user how far down the page they have read. There is a scroll bar on the right hand side of the page on my current device, but that isn't always present on all devices. It is possible to hide the scroll bar on Macs and mobile devices so that they are only shown as the page is being scrolled.

Putting this piece of information at the top of the page can be a useful indicator that augments already existing technologies.

To start, we need to add a little bit of markup to the page. This consists of a div that will be positioned at the top of the page and a second inner div to provide the progress bar. This markup can be placed anywhere on the page as it will be positioned absolutely.

<div id="reading-progress"><div id="reading-progress-fill"></div></div>

In order to make the progress bar work it needs some CSS adding to the page. I've split the styles into two parts in order to handle layout and theme separately.

The layout styles involve setting the progress bar to the top of the page and fixing it to the browser window. We also make sure that it appears on top of other elements by using a high z-index value.

/* Layout */
#reading-progress {
    position: fixed;
    width: 100%;
    height: 5px;
    z-index: 9999;
    top: 0;
    left: 0;

#reading-progress-fill {
    height: 5px;
    width: 0;

The theming styles are used to add presentation to the progress bar. This includes colouring it in red and creating transitions so that the bar can slide across the top of screen and not jump.

/* Theme */
#reading-progress-fill {
    -webkit-transition: width 100ms ease;
    -o-transition: width 100ms ease;
    transition: width 100ms ease;
    background-color: #ff0000;

The JavaScript behind the progress bar isn't that complicated. We just attach an event listener to the scroll event and use this to update the width of the inner reading-progress-fill div element. The width is worked out as a percentage of the height of the page and the amount of scroll from the top of the page.

const readingProgress = document.querySelector('#reading-progress-fill');
document.addEventListener('scroll', function(e) {
  let w = (document.body.scrollTop || document.documentElement.scrollTop) / (document.documentElement.scrollHeight - document.documentElement.clientHeight) * 100;'width', w + '%');

If you want the same effect in jQuery then I have also created the equivalent code below.

jQuery(window).scroll((function() {
  let w = (document.body.scrollTop || document.documentElement.scrollTop) / (document.documentElement.scrollHeight - document.documentElement.clientHeight) * 100;
  jQuery("#reading-progress-fill").css({width: w + "%"})

Both of these versions have the following effect on the page layout. As the user scrolls down the page the red bar fills across the screen from left to right.

Reading progress, showing the progress bar in action.

If you have a footer on your site then you might want to offset your height to not include that measurement. You can do this by just adding the footer height to the width calculation.

The following code assumes that you have a footer of 250 pixels high. This can easily be changed to a percentage if you are using relative heights.

const readingProgress = document.querySelector('#reading-progress-fill');
const footerHeight = 250;
document.addEventListener('scroll', function(e) {
  let w = (document.body.scrollTop || document.documentElement.scrollTop) / (document.documentElement.scrollHeight - document.documentElement.clientHeight - footerHeight) * 100;'width', w + '%');

If you are interested in running this code then I have created a codepen that pulls everything together needed for this reading progress bar to work.

Add new comment

The content of this field is kept private and will not be shown publicly.
15 + 5 =
Solve this simple math problem and enter the result. E.g. for 1+3, enter 4.
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.