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);
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}%)`;
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)`;
By the way, a
div
with the sameheight
andwidth
that has itsborder-radius
set to50%
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 -
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 -
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!
Top comments (20)
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.
🤦♂️😂
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.Unless you have an unusually large footer, why would the two be any different? Blog posts are traditionally one per page.
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
That sounds like a web browser but with extra steps :)
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)
Infinite scroll breaks browser scrollbar functionality, and trying to implement your own begs the question, why choose infinite scroll in the first place?
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 😋
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.
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!
Gotcha :)
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&ABut what if the site didn't hide the native scroll bar? Wouldn't that be better?
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.
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 ifisScrolling
, and if it isn't, go do the thing with arequestAnimationFrame
. The function within that keeps calling itself with anotherrequestAnimationFrame
, and keeps unsettingisScrolling
.Thanks for the input. Yes, that is a very good point I had not thought about.
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!
Thanks for this.
To all those saying the native scroll bar serves the purpose - I will offer a few scenarios where this approach might be better :
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.
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.
kkkmk