What I'm trying to do is implement a progress bar to indicate how close one is to the end of the page in vanilla JavaScript. However, I'm running into a few issues.
First of all, although the body
element is being scrolled, document.body.scrollTop
always returns 0
. I've sorted this out by using document.scrollingElement.scrollTop
.
It's been a while since I last implemented a feature like this, so I took to Stack Overflow and found this thread, which jogged my memory a bit. From what I remember about the last time I implemented this feature, the formula should be something like the following, which was mentioned in that thread.
const progressBar = document.getElementById('footer__progress_bar')
const totalValue = document.body.scrollHeight - window.innerHeight
document.body.onscroll = () => {
let currentValue = document.scrollingElement.scrollTop
let offset = (currentValue / totalValue) * 100 - 100 + '%'
progressBar.style.left = offset
}
Unfortunately something is off with the script above and I can't seem to figure out what it is. For some reason the value of offset
overshoots (and sometimes undershoots) the mark. I've created a CODEPEN and the overshoot problem is persisting, so it seems as if the issue is the formula itself. Nonetheless, when I look at the numbers (window.innerHeight, body.scrollTop, et cetera), none of them seem to add up. Below are the numbers.
window.innerHeight ..................... 779
document.body.clientHeight ............ 3210
document.body.offsetHeight ............ 3212
document.body.scrollTop .................. 0
document.scrollingElement.scrollTop ... 2646
I've also noticed some super strange behaviour. document.body.clientHeight
and document.body.offsetHeight
will randomly change values when I refresh the page, so that they're almost constantly jumping back and forth from X to Y
.
Notably, the body element itself has a height
of auto
and no vertical margins, albeit some of its children do have vertical margins. The main
element, which is where I'm embedding everything from the database, also has height: auto
, yet it also returns 0
when I check its scrollTop
.
Does anybody have any idea where I'm going wrong or why the numbers aren't adding up?
Please see the changes I have applied to your CODEPEN
body {
height: 300vh;
background: black
}
footer {
width: 100vw;
height: 20px;
position: fixed;
bottom: 0;
left: 0;
background: #fff
}
#footer__progress_bar {
height: 20px;
background: blue;
width: 0%;
text-align: center
}
<footer>
<div id="footer__progress_bar">0%</div>
</footer>
<script>
window.onscroll = function() { ScrollIndicator() };
function ScrollIndicator() {
var winScroll = document.body.scrollTop || document.documentElement.scrollTop;
var height = document.documentElement.scrollHeight - document.documentElement.clientHeight;
var scrolled = (winScroll / height) * 100;
document.getElementById( 'footer__progress_bar' ).style.width = scrolled + "%";
document.getElementById( 'footer__progress_bar' ).innerHTML = Math.round( scrolled ) + "%"
}
</script>
You can use the PrognRoll jQuery plugin:
Examples
Body demo on CodePen
<body>
<!-- Content -->
</body>
Display a scroll progress indicator:
$(function() {
$("body").prognroll();
});
or create a scroll indicator with CSS and JavaScript (without plugin).
See the live example below:
window.onscroll = function() {
ScrollIndicator()
};
function ScrollIndicator() {
var winScroll = document.body.scrollTop || document.documentElement.scrollTop;
var height = document.documentElement.scrollHeight - document.documentElement.clientHeight;
var scrolled = (winScroll / height) * 100;
document.getElementById("headerBar").style.width = scrolled + "%";
console.log(Math.round(scrolled * 100) / 100);
document.getElementById("footerBar").style.width = scrolled + "%";
document.getElementById("footerBar").innerHTML = Math.round(scrolled) + "%";
}
body {
height: 2000px;
text-align: center;
font-family: arial;
color: #333;
margin: 0px;
}
.header {
position: fixed;
top: 0;
z-index: 1;
width: 100%;
background-color: #f1f1f1;
}
.header h2 {
text-align: center;
}
.progress-container {
width: 100%;
height: 8px;
background: #ccc;
}
.progress-bar {
height: 8px;
background: linear-gradient(141deg, #0fb8ad 0%, #1fc8db 51%, #2cb5e8 75%);
width: 0%;
}
.content {
padding: 50px 0;
margin: 50px auto 0 auto;
width: 80%;
}
footer {
width: 100vw;
height: 20px;
position: fixed;
bottom: 0;
left: 0;
background: #ccc;
}
.footer-progress-bar {
height: 20px;
background: linear-gradient(141deg, #0fb8ad 0%, #1fc8db 51%, #2cb5e8 75%);
width: 0%;
text-align: center
}
<div class="header">
<h2>Scroll Indicator</h2>
<div class="progress-container">
<div class="progress-bar" id="headerBar"></div>
</div>
</div>
<div class="content">
<h2>Scroll down to see how it works</h2>
</div>
<footer>
<div class="footer-progress-bar" id="footerBar">0%</div>
</footer>