I want to make it so that elements with the class fade
fade in when they are first visible on the screen then fade out when they leave the screen.
I want this animation to only happen once.
Below is what I have tried.
.fade {
/* transition: opacity 0.9s ease-in;*/
opacity: 0;
}
.fade.visible {
transition: opacity 0.9s ease-in;
opacity: 1;
}
window.addEventListener('scroll', fade)
function fade()
{
let animation=document.querySelectorAll('.fade');
for (let i=0; i<animation.length; i++)
{
let windowheight=window.innerHeight;
let top=animation[i].getBoundingClientRect().top;
if (top < windowheight)
{
animation[i].classList.add('visible');
}
else
{
animation[i].classList.remove('visible');
}
}
}
Use the IntersectionObserver API instead of expensive scroll listeners!
Here's an example that triggers a classList change when an Element is in viewport - based on this answer , with the only difference that this one uses classList.add
instead of classList.toggle
:
const inViewport = (entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add("is-inviewport"); } }); }; const Obs = new IntersectionObserver(inViewport); const obsOptions = {}; //See: https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Intersection_observer_options // Attach observer to every [data-inviewport] element: const ELs_inViewport = document.querySelectorAll('[data-inviewport]'); ELs_inViewport.forEach(EL => { Obs.observe(EL, obsOptions); });
[data-inviewport] { /* FOR THIS DEMO ONLY */ width:100px; height:100px; background:#0bf; margin: 150vh 0; } /* inViewport */ [data-inviewport="fade"] { transition: opacity 2s; opacity: 0; } [data-inviewport="fade"].is-inviewport { transform: rotate(180deg); opacity: 1; }
Scroll down... <div data-inviewport="fade"></div> <div data-inviewport="fade"></div>
Other than removing the event listener (which seems like doesn't work for you for some reason) a simple change would be to add a global boolean flag which you toggle once after performing the animation. If the flag is true, just return early from the fade function
Something like this should work or at least be pretty close
let animationHappened = false; // flag to keep track of animation
function fade() {
// only add the visible class if the animation hasn't happened yet, else return early
if (animationHappened) return;
let animation = document.querySelectorAll('.fade');
for (let i = 0; i < animation.length; i++) {
let windowheight = window.innerHeight;
let top = animation[i].getBoundingClientRect().top;
if (top < windowheight) {
animation[i].classList.add('visible');
} else {
animation[i].classList.remove('visible');
}
}
animationHappened = true;
}
This way fade
should only animate once so long as animationHappened
isn't set to false
again anywhere else in your code.
First things first, it's pretty dang inefficient to do querySelectorAll
on every scroll, so assuming all those elements are always there, let's just grab them all right away.
We can turn the node list into an array so we can easily remove items as we go. When an element is visible, we just add the visible class and remove it from the array. Then when the array is empty just remove the listener.
To make the items invisible again, we can push the visible items to a second array and perform a similar process. We only remove that listener after all items have faded in then faded out.
const fadeInElements = Array.from(document.querySelectorAll('.fade')); const makeInvisible = []; let fadeInEmpty = false; window.addEventListener('scroll', fadeIn); window.addEventListener('scroll', fadeOut); function fadeIn() { const windowheight = window.innerHeight; for (let i = 0; i < fadeInElements.length; i++) { const element = fadeInElements[i]; const topOfElement = element.getBoundingClientRect().top; if (topOfElement < windowheight) { element.classList.add('visible'); makeInvisible.push(element); fadeInElements.splice(i, 1); if (fadeInElements.length === 0) { window.removeEventListener('scroll', fadeIn); fadeInEmpty = true; } } } } function fadeOut() { const windowheight = window.innerHeight; for (let i = 0; i < makeInvisible.length; i++) { const element = makeInvisible[i]; const topOfElement = element.getBoundingClientRect().top; if (topOfElement >= windowheight) { element.classList.remove('visible'); makeInvisible.splice(i, 1); if (makeInvisible.length === 0 && fadeInEmpty) window.removeEventListener('scroll', fadeOut); } } }
div { margin-top: 100px; }.fade { opacity: 0; }.visible { transition: opacity 0.9s ease-in; opacity: 1; }
<div>1</div> <div class="fade">2</div> <div class="fade">3</div> <div class="fade">4</div> <div class="fade">5</div> <div class="fade">6</div> <div class="fade">7</div> <div class="fade">8</div> <div class="fade">9</div> <div class="fade">10</div>
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.