The posted (and accepted) answer is technically correct, but nowadays there are more performance friendly ways to achieve this. If you want to trigger animations on more elements than one, watching these multiple elements can result in bad performance. See for example this question. disclaimer: This question also has my answer, I will only copy my own work in this anwser:
To solve this in a modern, performant way it's best to use Intersection Observer (IO) for this.
With IO you can watch one (or multiple) elements and react once they come into view or if they intersect each other.
To use IO you first have to first set the options for it, then define which element(s) to watch and last to define what exactly happens once the IO triggers.
Example (Taken from here), with one minimal modification: The author removed the IO even if the animation didn't happen yet. I moved the unobserve call inside the check if element is visible.
const SELECTOR = '.watched';
const ANIMATE_CLASS_NAME = 'animated';
const animate = element => (
element.classList.add(ANIMATE_CLASS_NAME)
);
const isAnimated = element => (
element.classList.contains(ANIMATE_CLASS_NAME)
);
const intersectionObserver = new IntersectionObserver((entries, observer) => {
entries.forEach((entry) => {
// when element's is in viewport,
// animate it!
if (entry.intersectionRatio > 0) {
animate(entry.target);
// remove observer after animation
observer.unobserve(entry.target);
}
});
});
// get only these elements,
// which are not animated yet
const elements = [].filter.call(
document.querySelectorAll(SELECTOR),
element => !isAnimated(element, ANIMATE_CLASS_NAME)
);
//console.log(elements);
// start observing your elements
elements.forEach((element) => intersectionObserver.observe(element));
.h100 {
height: 100vh;
}
.watched {
opacity: 0;
transition: opacity .5s;
}
.watched.animated {
opacity: 1;
}
<div class="h100">
scroll down
</div>
<div class="watched">
I'm watched
</div>
<div class="h100">
Another element, keep scrolling
</div>
<div class="watched">
I'm also watched
</div>