Scrolling Progress Bar - javascript

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>

Related

Vertical Scroll Indicator

Trying to create a Scroll Indicator, which would not be horizontal, but vertical. The problem is that when I try to start scrolling the bar that indicated the position isn't scaling...
HTML
<div id="scrollbar">
<div id="bar">
</div>
<div>
CSS
#scrollbar
{
width: 1%;
height: 100%;
padding: 0;
margin: 0 0 0 auto;
right: 0;
top: 0;
position: fixed;
visibility: visible;
background-color: transparent;
}
#scrollbar #bar
{
width: inherit;
height: 0%;
background-color: #ccc;
}
JS
var bar = document.getElementById("bar");
window.onscroll = function () { scrollIndicator() };
window.onload = function () { scrollIndicator() };
function scrollIndicator()
{
var winScroll = document.body.scrollTop || document.documentElement.scrollTop;
var height = document.documentElement.scrollHeight - document.documentElement.clientHeight;
var scrolled = 100;
if(height > 0)
{
scrolled = (winScroll / height) * 100;
}
bar.style.height = scrolled + "%";
}
I absolutely cannot find the problem...
your web page doesn't have enough content for it to scroll, hence scroll event isn't firing.
You need to fix the html, #scrollbar is missing a closing div tag and add enough content on the webpage so that it scrolls.
You also need to change the width of #bar from inherit to 100%.
var bar = document.getElementById("bar");
window.onscroll = function() {
scrollIndicator()
};
function scrollIndicator() {
var winScroll = document.body.scrollTop || document.documentElement.scrollTop;
var height = document.documentElement.scrollHeight - document.documentElement.clientHeight;
var scrolled = 100;
if (height > 0) {
scrolled = (winScroll / height) * 100;
}
bar.style.height = scrolled + "%";
}
#scrollbar {
width: 1%;
height: 100%;
padding: 0;
margin: 0 0 0 auto;
right: 0;
top: 0;
position: fixed;
background-color: transparent;
}
#scrollbar #bar {
width: 100%;
height: 0%;
background-color: blue;
}
.section {
height: 100vh;
}
.section:nth-child(even) {
background: green;
}
.section:nth-child(odd) {
background: red;
}
<div id="scrollbar">
<div id="bar"></div>
</div>
<div class="section"></div>
<div class="section"></div>
<div class="section"></div>

On horizontal scroll of overflown div, fill in progress bar

I have a div which contains an image. The container of this image has overflow:scroll, so that the user can scroll left or right to see the rest of the image.
I've also implemented a progress bar, which should indicate how much of the image remains to scroll. I.e. if the user has scrolled 5% to the right, it'll fill up 5% of the progress bar (and vice versa).
I can get the function working based on scrollHeight, but can't get it working based on scrollWidth.
Where am I going wrong?
window.onscroll = function() {myFunction()};
function myFunction() {
var winScroll = document.body.scrollTop || document.documentElement.scrollTop;
var width = document.documentElement.scrollLeft - document.documentElement.clientWidth;
var scrolled = (winScroll / width) * 100;
document.getElementById("myBar").style.width = scrolled + "%";
}
.imgCont {
background: black;
overflow: scroll;
position: relative;
}
.imgCont img {
width: auto;
max-width: none;
}
.progress-container {
width: 100%;
height: 8px;
background: blue;
}
.progress-bar {
height: 8px;
background: red;
width: 0%;
}
<div class="imgCont">
<img src="https://i.imgur.com/KhWo66L.png">
</div>
<div class="progress-container">
<div class="progress-bar" id="myBar"></div>
</div>
You need to add listeners on the .imgCont element and use it's scrollLeft, scrollWidth and clientWidth properties
let scrEl = document.getElementById("scr-el")
scrEl.addEventListener('scroll', event => {
let scrolled = (scrEl.scrollLeft / (scrEl.scrollWidth - scrEl.clientWidth) ) * 100
document.getElementById("myBar").style.width = scrolled + "%"
});
.imgCont {
background: black;
overflow-x: scroll;
position: relative;
}
.imgCont img {
width: auto;
max-width: none;
}
.progress-container {
width: 100%;
height: 8px;
background: blue;
}
.progress-bar {
height: 8px;
background: red;
width: 0%;
}
<div id="scr-el" class="imgCont">
<img src="https://i.imgur.com/KhWo66L.png">
</div>
<div class="progress-container">
<div class="progress-bar" id="myBar"></div>
</div>
windows.onscroll won't emit any events while you scroll horizontally because scroll is happening in the element with class imgCont.
put an id imgCont
<div class="imgCont" id="imgCont">
<img src="https://i.imgur.com/KhWo66L.png">
</div>
and call the on scroll event as
document.getElementById("imgCont").onscroll
Jquery solution setps:
subtract the visible width of the image and the real image width
var winScroll = $(".imgCont img").width() - $(".imgCont").width();
get the left scroll position
var width = $(".imgCont").scrollLeft();
get the percentage from the width and position
var scrolled = ((width / winScroll) * 100);
Check the snippet:
$(function(){
$(".imgCont").scroll(function(){
var winScroll = $(".imgCont img").width() - $(".imgCont").width();
var width = $(".imgCont").scrollLeft();
var scrolled = ((width / winScroll) * 100);
$("#myBar").width(scrolled + "%");
});
});
.imgCont {
background: black;
overflow: scroll;
position: relative;
height:200px;
}
.imgCont img {
width: auto;
max-width: none;
}
.progress-container {
width: 100%;
height: 8px;
background: blue;
}
.progress-bar {
height: 8px;
background: red;
width: 0%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="imgCont">
<img src="https://static.toiimg.com/photo/msid-67868104/67868104.jpg?1368689">
</div>
<div class="progress-container">
<div class="progress-bar" id="myBar"></div>
</div>
Another solution.
let div = document.getElementById("theDiv")
div.addEventListener('scroll', function(e) {
let inner = window.innerWidth
let left = div.scrollLeft
let sWidth = div.scrollWidth
let total = sWidth - inner
let width = 1 * left / total * 100
if (width >= 100) {
return document.getElementById("myBar").style.width = "100%";
}
document.getElementById("myBar").style.width = `${width}%`;
});
body {
margin: 0;
}
.imgCont {
background: black;
overflow: auto;
position: relative;
}
.imgCont img {
width: auto;
max-width: none;
}
.progress-container {
width: 100%;
height: 8px;
background: blue;
}
.progress-bar {
height: 8px;
background: red;
width: 0%;
}
<div class="imgCont" id="theDiv">
<img src="https://i.imgur.com/KhWo66L.png">
</div>
<div class="progress-container">
<div class="progress-bar" id="myBar"></div>
</div>

Changing opacity of header on scroll

I have some code which moves the header image when the user scrolls. It seems to work fine:
<script>
$(window).scroll(function(e) {
var scrolled = $(window).scrollTop(),
header = $('.site-header');
header.css('top', -(scrolled) * 0.5 + 'px');
if (scrolled > $(document).height() - $(window).height() - 470) {
var scrollPos = scrolled - ($(document).height() - $(window).height() - 470);
var position = scrollPos / 10;
var opacity = (scrollPos * 1) / 470;
}
});
</script>
I'm trying to get the opacity to change as well, so it fades out when I scroll down. This bit doesn't seem to work.
This full code is here:
https://jsfiddle.net/spadez/acz13129/1/
There seems to be something in the code already around opacity but it doesn't change and the console throws no errors so I am a bit stuck.
$(window).scroll(function(e) {
var scrolled = $(window).scrollTop(),
header = $('.site-header');
header.css('top', -(scrolled) * 0.5 + 'px');
var scrollPos = scrolled - ($(document).height() - $(window).height() - 470);
var position = scrollPos / 10;
var opacity = ((scrollPos / -1000) - 1.734) * 1.5; // Opacity is in decimals e.g. 0.5
$('.site-header').css({
"opacity": opacity,
"position": position + '%'
});
});
body {
margin: 0;
background: #f1f1f1;
}
header {
position: fixed;
top: 0;
width: 100%;
z-index: -1;
height: 500px;
line-height: 500px;
text-align: center;
margin: 0;
padding: 0;
}
.wrapper-parallax {
margin-top: 500px;
}
.content {
border-top: 5px solid white;
position: relative;
z-index: 1;
background: white;
min-height: 2800px;
background-image: url(https://i.imgur.com/DCXgzWc.png);
background-position-y: top;
background-repeat: no-repeat;
background-size: 300%;
background-position-x: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<div class="wrapper-parallax">
<header class="site-header">
<img src="img/ja.svg" width="250px" alt="Anna and James" class="logo">
</header>
<div class="content">
<h1>Content</h1>
</div>
</div>
Is this what you wanted?

Cover image position relative to text

I am trying to set a text to overlap an image but the position should stay same on all screen sizes.
Example:
Here is an example of what I have tried demo
.c-txt-on-img{
position: relative;
}
.c-txt-on-img .txt{
font-size: 30px;
font-weight: bold;
font-family: arial, sans-serif;
max-width: 200px;
position: absolute;
top: 80px;
left: 158px;
}
.c-txt-on-img .img {
width: 100vw;
height: 100vh;
background-size: cover;
background-position: center center;
}
<div class="c-txt-on-img">
<div class="txt">Tony where are you !!!!</div>
<div class="img" style="background-image: url(http://theprojectstagingserver.com/stackoverflow/txt-on-img/comic.jpg)"></div>
</div>
It works on a specific screen-size only, I can fix this on different sizes using different media queries but that will take too much time!
There are 2 main challenges:
1) Align the image and text to always stay on the same spot.
2) Aligning will leave extra uneven space on top/bottom & left/right side of the image so we need to increase image size enough that it covers the whole screen.
For first part we can define same top left position to text and image, then give a negative translate percentage to image so that top left origin of image is the same spot where the text bubble is.
Next we can calculate the space on right/left/top/bottom of image & increase its width till no negative space is left.
Below is a GIF image to explain this better:
Here is the DEMO
var viewportOffset = [],
winWidth,
winHeight,
inLoop = false,
resizeTimeout;
$(function(){
init();
});
$(window).resize(function(){
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(function(){
init();
}, 500);
});
function init() {
winWidth = $(window).width();
winHeight = $(window).height();
inLoop = false;
coverImage();
}
function coverImage() {
$('.js-cover-img').each(function (i) {
viewportOffset[i] = getViewportOffset($(this));
if(!inLoop){
$(this).width('auto');
$(this).height('auto');
}
var imgWidth = $(this).width();
var imgHeight = $(this).height();
viewportOffset[i].right = winWidth - imgWidth- (viewportOffset[i].left);
viewportOffset[i].bot = winHeight - imgHeight- (viewportOffset[i].top);
if(viewportOffset[i].top < 0){
var vertViewportOffest = viewportOffset[i].bot;
}else if(viewportOffset[i].bot <= 0){
var vertViewportOffest = viewportOffset[i].top;
}else{
var vertViewportOffest = viewportOffset[i].top + viewportOffset[i].bot;
}
if(viewportOffset[i].right < 0){
var horViewportOffest = viewportOffset[i].left;
}else if(viewportOffset[i].left < 0){
var horViewportOffest = viewportOffset[i].right;
}else{
var horViewportOffest = viewportOffset[i].left + viewportOffset[i].right;
}
if(horViewportOffest > 0 || vertViewportOffest > 0){
$(this).width(imgWidth + 20);
inLoop = true;
coverImage();
return false;
}
});
}
/* Get's the viewport position */
function getViewportOffset($e) {
var $window = $(window),
scrollLeft = $window.scrollLeft(),
scrollTop = $window.scrollTop(),
offset = $e.offset();
return {
left: offset.left - scrollLeft,
top: offset.top - scrollTop
};
}
body, html{
padding: 0;
margin: 0;
width: 100%;
height: 100%;
}
.c-txt-on-img {
position: relative;
width: 100vw;
height: 100vh;
overflow: hidden;
}
.c-txt-on-img .txt {
font-size: 30px;
font-weight: bold;
font-family: arial, sans-serif;
max-width: 200px;
position: absolute;
top: 30%;
left: 30%;
z-index: 2;
transform: translate(-50%, -50%);
text-align: center;
}
.c-txt-on-img .img {
transform: translate(-28.5%, -23%);
z-index: 1;
position: absolute;
top: 30%;
left: 30%;
min-width: 870px;
}
.c-txt-on-img img{
display:block;
width: 100%;
}
<script
src="https://code.jquery.com/jquery-1.12.2.min.js"
integrity="sha256-lZFHibXzMHo3GGeehn1hudTAP3Sc0uKXBXAzHX1sjtk="
crossorigin="anonymous"></script>
<div class="c-txt-on-img">
<div class="txt">Tony where are you !!!!</div>
<div class="img js-cover-img">
<img src="http://theprojectstagingserver.com/stackoverflow/txt-on-img/comic.jpg">
</div>
</div>

Resize Div whilst maintaining ratio

I'm trying to create a page where a centrally placed div resizes to fit in the page both horizontally and vertically whilst retaining the ratio.
Currently I'm using a combination of JS and CSS although I'm not sure it's particularly efficient - it also seems a little hacky.
How would I either clean up this code or rewrite it to achieve this?
Javascript
function changesize(){
var $width = $(".aspectwrapper").height();
var $changewidth = $width * 1.5;
var $doc = $(document).width();
var $height;
if ($doc <= $changewidth){ $changewidth = ( $doc - 100 );
$height = ( ($doc - 100) / 1.5 );
$(".aspectwrapper").css('height', "" + $height + "");
};
$(".content").css('width', "" + $changewidth + "");
$(".aspectwrapper").css('width', "" + $changewidth + "");
};
$(document).ready(function(e) {
$(changesize);
});
$(window).resize(function(e) {
$(changesize);
});
CSS
html, body {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
.aspectwrapper {
display: inline-block;
position: relative;
margin: 0 auto;
height: 90%;
border: #F00 1px solid;
background-color: #F00;
}
.content {
position: absolute;
top: 0;
bottom: 0;
right: 0;
margin-top: 60px;
outline: thin dashed green;
overflow: hidden;
background-color: #09C;
}
#wrapper {
width: 100%;
height: 100%;
text-align: center;
background-color: #0F3;
float: left;
}
HTML
<div id="wrapper">
<div class="aspectwrapper">
<div class="content"> CONTENT GOES HERE</div>
</div>
<div style="clear:both;"></div>
</div>
I'd personally position my center div horizontally and vertically using positioning/margin combinations with a specific liquid width and height. It would resize based on page width without the use of javascript. Here's an example.
http://jsfiddle.net/9Aw2Y/

Categories

Resources