javascripthtmlreactjsintersection-observer

.querySelectorAll() returning an empty nodelist React js


Hi I'm new here and I was trying to learn react js along with intersection observer api, I was trying to get all elements with .cards class using .querySelectorAll(".cards");, my problem is that it was returning an empty nodelist instead of a list of my elements. I thought its on the initializing of the elements on my components so I tried putting defer attribute on my script and put it below the root elemen but it didnt work either.

HTML

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title> Intersection Obsever </title>
  </head>
  <body>
    <div id="root"></div>
    <script defer type="module" src="/src/main.jsx"></script>
    <script defer src="/src/Intersection Observer/intersection-observer.js"></script>
    <script defer src="/src/Intersection Observer/Observer.js"></script>
  </body>
</html>

App Component

import CardHolder from './Components/CardHolder'
import './App.css'

function App() {
  return (
    <>
      <CardHolder/>
    </>
  )
}

export default App

CardHolder Component

import Cards from "./Cards";

function CardHolder() {
    return (
        <div className="cardHolder">
            <h1 style={{color: "lightblue"}}> Cards </h1>
            <Cards/>
        </div>
    )
}

export default CardHolder;

Cards Component

function Cards() {
    return (
        <>
            <div className="cards"> Cards </div>
            <div className="cards"> Cards </div>
            <div className="cards"> Cards </div>
            <div className="cards"> Cards </div>
            <div className="cards"> Cards </div>
            <div className="cards"> Cards </div>
            <div className="cards"> Cards </div>
            <div className="cards"> Cards </div>
            <div className="cards"> Cards </div>
            <div className="cards"> Cards </div>
            <div className="cards"> Cards </div>
            <div className="cards"> Cards </div>
            <div className="cards"> Cards </div>
            <div className="cards"> Cards </div>
            <div className="cards"> Cards </div>
            <div className="cards"> Cards </div>
            <div className="cards"> Cards </div>
            <div className="cards"> Cards </div>
            <div className="cards"> Cards </div>
            <div className="cards"> Cards </div>
            <div className="cards"> Cards </div>
        </>
    )
}

export default Cards;

Observer js

const observer = new IntersectionObserver(entries => {
    entries.forEach(en => {
        if (en.isIntersecting) {
            en.target.classList.add("show");
        } else { en.target.classList.remove("show");} 
    })
}, {
    threshold: 0.5
})

const cards = document.querySelectorAll(".cards");
cards.forEach(card => {
    observer.observe(card);
})

I was trying get the cards elements and put it into the card variable so I can observe every each of them using forEach


Solution

  • One example would be to use a hook.

    // useCardObserver.js
    
      const makeObserver = () => {
           return new IntersectionObserver(entries => {
              entries.forEach(en => {
                  if (en.isIntersecting) {
                      en.target.classList.add("show");
                  } else { en.target.classList.remove("show");} 
              })
          }, {
              threshold: 0.5
          })
      }
    
    const useCardObserver = (divRef) => {
      const [observer, setObserver] = useState(makeObserver())
    
      useEffect(() => {
       if (divRef.current) {
          const cards = divRef.current.querySelectorAll(".cards");
          cards.forEach(card => {
              observer.observe(card);
          })
       }
       
       return () => {
          if (divRef.current) {
            const cards = divRef.current.querySelectorAll(".cards");
            cards.forEach(card => {
                observer.unobserve(card);
            })
          }
       }
      }, [divRef.current])
      
      return null
    
    }
    

    And call it inside your Cards component using a ref as argument.

    function Cards() {
        const ref = useRef()
        
        useCardObserver(ref)
        
        return (
            <div ref={ref}>
                <div className="cards"> Cards </div>
                <div className="cards"> Cards </div>
                <div className="cards"> Cards </div>
                <div className="cards"> Cards </div>
            </div>
        )
    }
    
    export default Cards;