0

I know its easy to have triggers on scroll and to add class name to HTML elements through jquery, but I was wondering if it's possible (or even worth it) to try to do so in react or vanilla javascript. I am learning react and want to trigger animations when users have scrolled a certain distance of the web page. I have not used jquery so far as many people advise that you should stick to vanilla javascript as much as possible as it is very powerful and you won't require importing the jquery libraries.

I have implemented a scroll trigger in vanilla JS successfully with the following code bellow, although its very inefficient as it uses an event listener on the scroll (maybe Jquery does this anyway though?). Is jquery simply the best way to do what I want? Thanks in advance guys, here is the spaghetti code:

function getDocHeight(D) {
  return Math.max(
      D.body.scrollHeight, D.documentElement.scrollHeight,
      D.body.offsetHeight, D.documentElement.offsetHeight,
      D.body.clientHeight, D.documentElement.clientHeight
  )
}

  function amountscrolled(){
var winheight= window.innerHeight || (document.documentElement || document.body).clientHeight
var docheight = getDocHeight(document)
var scrollTop = window.pageYOffset || (document.documentElement || document.body.parentNode || document.body).scrollTop
var trackLength = docheight - winheight
var pctScrolled = Math.floor(scrollTop/trackLength * 100) // gets percentage scrolled (ie: 80 or NaN if tracklength == 0)

if (pctScrolled > 50) {
  document.getElementById('anim').className+=" animate";
}
}

window.addEventListener("scroll", function(){
amountscrolled()
}, false)
Tamjid
  • 4,326
  • 4
  • 23
  • 46

1 Answers1

0

This is an old question, but the only existing answer is completely unrelated, and there is a great way to do this now – the IntersectionObserver API.

const callback = (entries) => 
  entries.forEach(entry => 
    entry.isIntersecting && entry.target.classList.add("show")
  );

const observer = new IntersectionObserver(callback);
const animate = document.querySelectorAll(".animate");
animate.forEach(div => observer.observe(div));
body {
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
body > * + * { margin-top: 1em }
#content {
  height: 100vh;
  background: red;
}
.animate {
  height: 20vh;
  background: blue;
  opacity: 0;
  transition: opacity 1s;
}
.animate.show {opacity: 1}
<div id="content">content</div>
<div class="animate">animate me 1</div>
<div class="animate">animate me 2</div>
<div class="animate">animate me 3</div>

The best part is that for your React needs, there is even a library you can use! react-intersection-observer

$ npm install react-intersection-observer --save
$ # or
$ yarn add react-intersection-observer

(from the README)

import React from 'react';
import { useInView } from 'react-intersection-observer';

const Component = () => {
  const { ref, inView, entry } = useInView({
    /* Optional options */
    threshold: 0,
  });

  return (
    <div ref={ref}>
      <h2>{`Header inside viewport ${inView}.`}</h2>
    </div>
  );
};