I have a fade in function in js for a website, to be activated when the element is in the vertical viewport. Thing is, the element has to be completely in the viewport for the function to activate, and as I also set the element to move up while it fades in, you have to scroll a lot for it to work, which at times create a lot of whitespace that doesn't look good. Here's the js:
(function() {
'use strict';
// define variables
var items = document.querySelectorAll(".fadeup");
// check if an element is in viewport
function isElementInViewport(el) {
var rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight -50)
);
}
function callbackFunc() {
for (var i = 0; i < items.length; i ) {
if (isElementInViewport(items[i])) {
items[i].classList.add("in-view");
}
}
}
// listen for events
window.addEventListener("load", callbackFunc);
window.addEventListener("resize", callbackFunc);
window.addEventListener("scroll", callbackFunc);
})();
And the css:
.fadeup {
visibility: hidden;
opacity: 0;
position: relative;
top: 30px;
transition: opacity 1s, top 1s, visibility 1s;
}
.fadeup.in-view {
top: 0px;
transform: none;
visibility: visible;
opacity: 1;
}
How can I change the offset of the function, for it to be activated before the element is fully in the viewport? Maybe just halfway in, or a set amount of pixels in? I can't seem to find the way.
Thanks!
CodePudding user response:
Your logic is almost there. You just modify the condition to check the bottom part of rect
in isElementInViewport
like below
// check if an element is in viewport
function isElementInViewport(el) {
var rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
//calculate from the top a half of element
rect.top (rect.height/2) <= (window.innerHeight || document.documentElement.clientHeight)
);
}
Full testing code
(function() {
'use strict';
// define variables
var items = document.querySelectorAll(".fadeup");
// check if an element is in viewport
function isElementInViewport(el) {
var rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.top (rect.height/2) <= (window.innerHeight || document.documentElement.clientHeight)
);
}
function callbackFunc() {
for (var i = 0; i < items.length; i ) {
if (isElementInViewport(items[i])) {
items[i].classList.add("in-view");
}
}
}
// listen for events
window.addEventListener("load", callbackFunc);
window.addEventListener("resize", callbackFunc);
window.addEventListener("scroll", callbackFunc);
})();
.fadeup {
visibility: hidden;
opacity: 0;
position: relative;
top: 30px;
transition: opacity 1s, top 1s, visibility 1s;
height: 300px;
border: 1px solid #ccc;
}
.fadeup.in-view {
top: 0px;
transform: none;
visibility: visible;
opacity: 1;
}
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
We also can have another effective way to control element visibility with IntersectionObserver
Full example here
(function() {
'use strict';
var items = document.querySelectorAll(".fadeup");
let observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add("in-view");
}
});
}, {
threshold: 0.5 //whenever the element is visible on a half of the screen
});
items.forEach(p => {
observer.observe(p)
});
})();
.fadeup {
visibility: hidden;
opacity: 0;
position: relative;
top: 30px;
transition: opacity 1s, top 1s, visibility 1s;
height: 300px;
border: 1px solid #ccc;
}
.fadeup.in-view {
top: 0px;
transform: none;
visibility: visible;
opacity: 1;
}
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
<div >
Content
</div>
BUT one thing I'd like to note down that IntersectionObserver
does not fully support for all browsers (you can check browser compatibility here)
CodePudding user response:
Look into doing an IntersectionObserver. In addition to being much more efficient about observing when an element is visible, it also has settings options that let you set how much of the element is visible before firing off.