Scrolling Progress Bar

2019-08-24 04:13发布

问题:

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?

回答1:

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>



回答2:

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>