DEV Community

Cover image for Page scroll progress bars
debadeepsen
debadeepsen

Posted on • Updated on

Page scroll progress bars

You must have seen this on blogs, or articles of other kinds (what's the difference again?). Basically, you are (perhaps correctly) assumed to be one of those readers who are either super-busy or super-lazy, and therefore need to know how much of the page you have visually consumed. So there's some kind of a progress bar that tells you how much you've scrolled. It looks cool, helps your readers/users, and is super simple to implement. Let's dig right in.

(If you're viewing this post on https://debadeepsen.com/blog/page-scroll-progress-bars-fjl/, you can already see this in action)

Progress percent

Before we get into any progress bar, we need to calculate the progress percent, aka, how much of the page has been scrolled. Thanks to the helpful people at CSS Tricks, I was able to compute that easily. The following code does exactly that.

let scrollTop = window.scrollY;
let docHeight = document.body.offsetHeight;
let winHeight = window.innerHeight;
let scrollPercent = scrollTop / (docHeight - winHeight);
let scrollPercentRounded = Math.round(scrollPercent * 100);
Enter fullscreen mode Exit fullscreen mode

Horizontal progress bar

This one's the simpler one of the two. All we need to do here is have a div "stick" to the top of the page, and set its background to display a visual indication of the progress. The trick here is to use the linear-gradient() CSS function, which you can read up all about here. Now, instead of a smooth gradient, what we need to achieve is a distinct color change at certain point. For that, we can use the "linear-color-stop" overload (somebody please check if they're still called function overloads in CSS, I honestly don't know for sure). If we give the gradient two colors, and set the progress percent as the color-stop for both, the result will be a distinct change of color. Since we'll be using JavaScript to set it, here's what that will look like:

document.querySelector("pb").style.background = 
   `linear-gradient(to right, #498 ${scrollPercentRounded}%, #eee ${scrollPercentRounded}%)`;
Enter fullscreen mode Exit fullscreen mode

The greenish "progress bar" isn't overlain on top of the grey bar, but using linear-gradient this way gives the impression that it does.

Now if you add the JavaScript code above to the onscroll event handler for the page (be patient, a TL;DR CodeSandbox is at the end of this article, as usual), you should be able to see the scroll progress bar in action.

Circular progress indicator

The circular progress bar(?!) is similar, with a little more coding effort added. We'll still be using gradients, but this time, we're going to use something called the "conic gradient". Which, as the name suggests, displays a gradient to make it appear that... um, you're staring at a cone downwards from top? (My description skills aren't astounding, so why don't you look it up on the official docs?) But the principle remains the same - we define "hard stops" so that the gradient looks less like a smooth gradient and more like a sharp change in color. That means setting the background programmatically in the following way.

// using variables from the code block above
let scrollPercent = scrollTop / (docHeight - winHeight);
let scrollPercentRounded = Math.round(scrollPercent * 100);
let degrees = scrollPercent * 360;

document.querySelector(".bg").style.background = `conic-gradient(#498 ${degrees}deg, #ddd ${degrees}deg)`;
Enter fullscreen mode Exit fullscreen mode

By the way, a div with the same height and width that has its border-radius set to 50% will be rendered as a perfect circle.

Now, if we implement a conic gradient on a circle, it will look like a pie chart, like this -
Imgur

Now, you could keep the pie progress chart, but if you wanted, you could also overlay a circular disc on top of it using the right position attribute and dimensions, and it would look like so -
Imgur

And that's basically it. Thank you for reading through this patiently (and if you literally just jumped to this section for the full code, I don't hate you either - we're all busy). Here's the Sandbox. Happy coding!

Latest comments (20)

Collapse
 
d7460n profile image
D7460N

This is great in that it shows a non-dependent way to implement functionality that can be used in combination with other functionality such as adding a scroll-to-top button in the middle of the circular progress scrollbar, as demonstrated here - which is what I am attempting to build (unsuccessfully) in pure CSS.
codepen.io/dragontheory/pen/yLMKYmG

This combination could be convenient for users on mobile platforms or for small content subsections with overflowing content that would otherwise be difficult to control. Knowing when to apply innovation to convention for a better experience is the science of usability.

This has given me some good ideas (which is what this community is all about - sharing ideas).

Thank you for sharing!

Collapse
 
debadeepsen profile image
debadeepsen

To all those saying the native scroll bar serves the purpose - I will offer a few scenarios where this approach might be better :

  1. In general, your gaze is likely to be on the content of the page, not to the right of the screen where vertical scrollbars are. Having a scrollbar near the content makes it easier for the user to catch it with their peripheral vision. And the scroll bar isn't a "progress bar" as such - as in, it's not a progress meter displaying the progress percent, it's a moving box, so the visual representations are different to start with.

  2. Remember, because this meter is coded using CSS and JavaScript, you have complete freedom over how much of the page you actually consider to be relevant content. For example, on this particular dev.to article, with 18 comments, the comment section takes up more than half the vertical space. What if I was interested in knowing where the end of the post is (so that I can just jump to the sandbox and copy the code because otherwise my boss will fire me in a day), rather than how much the entire height of the document is? The native scroll bar will only show its location relative to the size of the whole document. Using a progress indicator like this could then alleviate that problem.

Collapse
 
matthijsewoud profile image
⚡️

Looks good, but don't you want to debounce it? Doing onscroll events, even when something has just been draw in the same time-frame, is lots of extra calculations that slow it down.

If you'd use something like requestAnimationFrame, you can cut down on CPU/GPU usage dramatically, or even turn it off completely if the user isn't actively watching it.

You can implement it in a way the onscroll it'll check if isScrolling, and if it isn't, go do the thing with a requestAnimationFrame. The function within that keeps calling itself with another requestAnimationFrame, and keeps unsetting isScrolling.

Collapse
 
debadeepsen profile image
debadeepsen

Thanks for the input. Yes, that is a very good point I had not thought about.

Collapse
 
shrey7070 profile image
Shrey soni

Thanks for this.

Collapse
 
silvideastora profile image
silvideastora

kkkmk

Collapse
 
moopet profile image
Ben Sinclair

So there's some kind of a progress bar that tells you how much you've scrolled.

Isn't this... the scroll bar? Am I missing something, or does this add a redundant indicator to your sandbox demo?

I mean, I appreciate the technique and everything, but this is a problem that already has a solution, and it's available in every browser by default.

Collapse
 
virtualvivek profile image
Vivek Verma • Edited

Scroll progress bar is really helpful when the site hides the natural scroll func. from the user and it scrolls on some logic:
Example: Online Courses or Q&A

Collapse
 
moopet profile image
Ben Sinclair

But what if the site didn't hide the native scroll bar? Wouldn't that be better?

Collapse
 
bubster profile image
TvO

I agree. Have always found it a stupid addition to blog websites. I don't see why it matters how far i've scrolled down.

Collapse
 
abhilearnstocode profile image
Abhii

🤦‍♂️😂

Yes it is scroll bar but we can implement in blogs where it will progress for the blog height and NOT the body height and hence can improve reading experience.

Collapse
 
moopet profile image
Ben Sinclair

Unless you have an unusually large footer, why would the two be any different? Blog posts are traditionally one per page.

Thread Thread
 
abhilearnstocode profile image
Abhii

Scroll progress bars or whatever they are called are something different than usual scrollbars. They are meant to please user and make your page look good. Unless it doesn't distract user or harms UX it's well and good.

And yea..
It's a scrollbar but just kept horizontal and sticky to the top

Thread Thread
 
moopet profile image
Ben Sinclair

That sounds like a web browser but with extra steps :)

Thread Thread
 
grahamthedev profile image
GrahamTheDev • Edited

A use case is for blogs where they have infinite scroll leading to the next post.

Not that I agree with infinite scroll but that is a valid use case as you can have a progress bar for each.

Other than that I do agree that there is little benefit (although aesthetically I do quite like the circular progress meter)

Thread Thread
 
moopet profile image
Ben Sinclair

Infinite scroll breaks browser scrollbar functionality, and trying to implement your own begs the question, why choose infinite scroll in the first place?

Thread Thread
 
grahamthedev profile image
GrahamTheDev

Hence why I said I don't agree with it, not a fan of infinite scroll myself due to accessibility issues it causes, but we are currently conversing on a site that uses infinite scroll for the home page feed so I suppose beggars can't be choosers 😋

Thread Thread
 
moopet profile image
Ben Sinclair • Edited

The home page here breaks browser functionality, but it's not really a problem because we're not reading a single article. The proposed solution in this post doesn't apply as far as I can tell unless you want a little indicator of your position in every blog post - and in turn that requires every blog post to be rendered in full rather than as a teaser card, otherwise they'd all say you were 100% of the way through.

Thread Thread
 
grahamthedev profile image
GrahamTheDev • Edited

I think I am not being clear, that part was me joking about the prevalence of infinite scroll, I was not meaning to link it to the article or having infinite scroll on actual posts.

I am also agreeing with you that there is no need for this on most sites and all i offered was one scenario (one which I am not a fan of!) where this could be useful as scroll position couldn’t be correlated with article length.

Hope we are clear now as we arguing a point from the same side of the fence!

Thread Thread
 
moopet profile image
Ben Sinclair

Gotcha :)