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!
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 class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
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 class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
Content
</div>
<div class="fadeup">
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)