I have a block that appears after scrolling 200px on the page
const navShareLink = document.getElementById('navShareLink');
document.addEventListener('scroll', function(e) {
if(window.scrollY > 200 && window.innerWidth > 576) {
navShareLink.style.display = "block";
} else {
navShareLink.style.display = "none";
}
});
.article-wrapper {
min-height: 200vh;
}
.article-wrapper p:first-of-type {
margin-top: 0;
}
footer {
min-height: 100vh;
background-color: #eee;
}
.sidemenu-shares {
z-index: 999;
display: flex;
flex-direction: column;
align-items: center;
height: 100%;
justify-content: center;
position: fixed;
top: 0;
right: 0;
flex-wrap: nowrap;
gap: 40px;
}
.rectangle {
z-index: 998;
transition: opacity 0.5s;
padding: 5px;
height: 106px;
width: 23px;
background-color: rgba(0, 0, 0, 0.1);
border-radius: 24px;
}
<div class="article-wrapper">
<div class="sidemenu-shares">
<div id="navShareLink" class="rectangle">
</div>
</div>
<div class="main-banner">
<h1>Title</h1>
</div>
<div>Main content...</div>
<div>Main content...</div>
<div>Main content...</div>
<div>Main content...</div>
<div>Main content...</div>
<div>Main content...</div>
<div>Main content...</div>
<div>Main content...</div>
<div>Main content...</div>
</div>
<footer>
Footer...
</footer>
But when it reaches the end of the page where the footer is, I need it to hide back so that it does not climb into the footer
It's easy to do if the page has the same height, but for me it can be either 2500px or 5000px
Is it possible to somehow count the pixels in reverse order, but not from the beginning of the page, but let's say 300px from the end?
I tried to get the height of the whole page and subtract 300
I used document.documentElement.scrollHeight
I thought I would just get a digital value like 4500, but in the end I get the output of this value in a pop-up window when I open the page
Alternatively, i can somehow hide this block when it touches the footer, is it possible to do it somehow?
This API allows you to observe elements on your page and invoke code based on their position relative to your current viewport.
Example:
const callback = (entries, observer) => {
entries.forEach(entry => {
// Each entry describes an intersection change for one observed
// target element:
// entry.boundingClientRect
// entry.intersectionRatio
// entry.intersectionRect
// entry.isIntersecting <- This is what you usually look out for
// entry.rootBounds
// entry.target
// entry.time
if (entry.isIntersecting) {
document.getElementById('navShareLink').style.display = 'none';
});
};
const options = {
root: document.body,
// This specifies a margin around the target (basically changes the size of the target) - can be negative
rootMargin: '0px 0px 0px 0px',
// This specifies how much of the target needs to be in the viewport to qualify as 'intersecting' (1 === 100%)
threshold: 1.0
}
const observer = new IntersectionObserver(callback, options);
const target = document.querySelector('footer');
observer.observe(target);
One observer can observe an arbitrary number of targets. It is usually the most performant way to handle this sort of thing.
Scroll Event handlers usually trigger very often and are thus quite inefficient. If you use scroll-handlers you should at the very least debounce them.
I didn't quite understand what you want but just in case you want the sidebar to be sticky and when you go down to the footer it is above that block you have to get the sidebar code out of div class="main-content" like that
/* The important part: */
.sidebar {
float: right;
position: sticky;
top: 8px;
/* This should match the top margin/padding on the body */
}
/* Cosmetics for the demo: */
.main-content {
min-height: 200vh;
}
.main-content p:first-of-type {
margin-top: 0;
}
.sidebar {
width: 25vw;
min-height: 25vh;
background-color: #a00;
}
footer {
min-height: 100vh;
background-color: #eee;
}
html {
margin: 0;
padding: 0;
}
body {
margin: 0;
padding: 8px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- here sidebar -->
<div class="sidebar">
Sidebar...
</div>
<div class="main-content">
<div>Main content...</div>
<div>Main content...</div>
<div>Main content...</div>
<div>Main content...</div>
<div>Main content...</div>
</div>
<footer>
Footer...
</footer>
</body>
</html>
To solve this we need to get appropriate properties from key elements:
document.body.scrollHeight
- here html has the scrollbar and body is overflowing. scrollHeight gives height of the entire document with overflow.footer.offsetHeight
- actual height of the footer.wrapper.getBoundingClientRect().y
- this gives y position of wrapper when we scroll down. It's negative cos the wrapper goes up.Remaining math is simple:
var sideMenu, footer, wrapper, nsl;
document.addEventListener("DOMContentLoaded", init, false);
function check() {
var range = document.body.scrollHeight - footer.offsetHeight;
var position = (nsl.offsetHeight + nsl.getBoundingClientRect().y - wrapper.getBoundingClientRect().y).toFixed(1);
nsl.innerText =
'\n Range: ' + range +
'\n Position: ' + position;
if (window.scrollY > 200 && (range > position)) {
nsl.style.visibility = "visible";
} else {
nsl.style.visibility = "hidden";
}
};
function init() {
sideMenu = document.getElementById('sideMenu');
footer = document.getElementById('footer');
wrapper = document.getElementById('wrapper');
nsl = document.getElementById('navShareLink');
window.onscroll = check;
check();
}
.article-wrapper {
min-height: 200vh;
position: relative;
top: 0;
left: 0;
}
.article-wrapper p:first-of-type {
margin-top: 0;
}
footer {
min-height: 100vh;
background-color: lightskyblue;
}
.sidemenu-shares {
z-index: 999;
display: flex;
flex-direction: column;
align-items: center;
height: 100%;
justify-content: center;
position: fixed;
top: 0;
right: 0;
flex-wrap: nowrap;
gap: 40px;
}
.rectangle {
z-index: 998;
transition: opacity 0.5s;
padding: 5px;
height: 106px;
width: 123px;
background-color: rgba(200, 0, 0, 0.1);
border-radius: 24px;
}
.content {
height: 50px;
border: 1px dotted gray;
}
<div id="wrapper" class="article-wrapper">
<div id='sideMenu' class="sidemenu-shares">
<div id="navShareLink" class="rectangle">
</div>
</div>
<div class="main-banner">
<h1>Title</h1>
</div>
<div class='content'>Main content...</div>
<div class='content'>Main content...</div>
<div class='content'>Main content...</div>
<div class='content'>Main content...</div>
<div class='content'>Main content...</div>
<div class='content'>Main content...</div>
<div class='content'>Main content...</div>
<div class='content'>Main content...</div>
<div class='content'>Main content...</div>
</div>
<footer id='footer'>
Footer...
</footer>
Check the output in full page mode.