How to Build a Page Scroll Progress Indicator With jQuery and SVG


Today we will be looking at a few techniques we can use to show scroll progress for users who are reading a page. This technique is being used on an increasing number of sites, and for good reason; it provides a contextual understanding of investment needed to consume a particular page. As the user scrolls, they are presented with a sense of current progress in different formats. 

As seen on

Today, we will cover two specific techniques you can employ to show scroll progress, and leave you with a toolset to create your own. Let's get started!

Setting up the Document

First, we will set up a mock document which will act as our post page. We will be using normalize.css and jQuery, as well as a Google font. Your empty HTML file should look like this:

Next, we will add our fake post content:

This gives us enough content to test our scrolling behaviors.

Basic Styling

We're going to use some basic styling to make our post a little more attractive.

Scroll Position Calculation

To calculate our scroll position, we need to understand conceptually what we are tracking. Since JavaScript can track only the top scroll value, we will need to track our scroll value from 0 (at the top, not scrolled) to whatever the final scroll value is. That final scroll value will be equal to the total document length minus the height of the window itself (because the document will scroll until the bottom of the document reaches the bottom of the window).

We will use the following JavaScript to calculate this scroll position.

The above code sets the window height and the body height, and when the user scrolls it uses those values to set a perc variable (short for percentage). We also utilize Math.min and Math.max to limit the values to the 0-100 range.

With this percentage calculation, we can drive the progress indicator.

Circle Indicator

The first indicator we will create is an SVG circle. We will utilize the SVG stroke-dasharray andstroke-dashoffset properties to show progress. First, let's add the progress indicator to the document.

This markup gives us two circles in an SVG, as well as a containing div to show our percentage count. We need to add style to these elements as well, and then we'll explain how these circles are positioned and animated.

These styles set us up to animate our circle element. Our progress should always be visible, so we set position to fixed on the .progress-indicator class, with positioning and sizing rules. We also set our progress count to be centered both vertically and horizontally inside this div.

The circles are positioned in the center using transform on the SVG elements themselves. We start the center of our circles using transform. We use a technique here that allows us to apply a rotation from the center of our circles in order to start the animation at the top of the circle (rather than the right side of the circle). In SVG, transforms are applied from the top left of an element. This is why we must center our circles at 0, 0, and move the circle's center to the center of the SVG itself using translate(50, 50).

Using stroke-dasharray and stroke-dashoffset

The properties stroke-dasharray andstroke-dashoffset allow us to animate the stroke of an SVG. stroke-dasharray defines the visible pieces of a stroke.  stroke-dashoffset moves the start of the stroke. These attributes combined allow us to create a stroke "keyframing" process.

Updating stroke-dasharray on Scroll

Next, we will add a function to update the stroke-dasharray on scroll, using our percentage progress previously shown.

The offset that matches our circle happens to be about 126. It's important to note that this won't work for all circles, as 126 is about the circumference of a circle with a radius of 20. To calculate the stroke-dashoffset for a given circle, mutiply the radius by 2PI. In our case, the exact offset would be 20 * 2PI = 125.66370614359172.

Horizontal Progress Variation

For our next example, we'll make a simple horizontal bar fixed to the top of the window. To accomplish this, we'll use an empty progress indicator div.

Note: we've added the "-2" to allow us to include this example in the same CSS file.

Next, we'll add our styling for this element.

Finally, we will set the width of the progress bar on scroll.

All together, our final JavaScript should look like this:

Other Ideas for Progress Bars

This article is intended to give you the tools and inspiration to design your own scroll progress solutions. Other ideas for progress bars might include using more descriptive or humanized terms for the progress indication itself, such as "halfway there" or "just getting started". Some implementations (like the example shown previously) use estimation of an article's read time. This could be estimated using code similar to the following:

You would then use the minCount in conjunction with the perc variable we are updating on scroll to show the reader their remaining time to read the article. Here's a very basic implementation of this concept.

One Final Piece: Adaptive Screen Sizing

To ensure that our progress indicator works as it should, we should make sure our math is calculating the right things at the right times. For that to happen, we need to make sure we are re-calculating the heights and updating the progress indicator when the user resizes the window. Here's an adaptation of the JavaScript to make that happen:

This code declares a function which sets the variables we need to calculate the progress at any given screen size, and calls that function on resize. We also re-trigger scroll on window resize so that our updateProgress function is executed.

You've Reached the End!

Having laid the foundation for any number of variants, what can you come up with? What progress indicators have you seen that work? How about indicators that are bad for usability? Share your experiences with us in the comments!