Observer intersection

무한스크롤, 이미지 지연로딩 등을 구현하는데 쓰이는 Observer intersection의 사용법을 알아보겠습니다

개념은 MDN에 잘 나와있으니, 간단한 코드를 통해서, 실제로 어떤식으로 동작하는지 조금 자세히 알아보겠습니다.

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .root {
        width: 300px;
        height: 500px;
        overflow: scroll;
        border: 5px solid pink;
      }

      li {
        width: 100%;
        height: 200px;
        background-color: grey;
        margin-bottom: 5px;
      }

      .vertical {
        color: white;
        font: 32px "Arial";
      }
    </style>
  </head>
  <body>
    <div class="root">
      <div class="list">
        <li>아이템1</li>
        <li>아이템2</li>
        <li>아이템3</li>
        <li>아이템4</li>
        <li>아이템5</li>
        
      </div>
    </div>
  </body>
  <script src="./index.js"></script>
</html>

index.js

const root = document.querySelector(".root");

const options = {
  root: root, // .root class를 가진 엘리먼트를 감시범위로 설정. null일 경우 브라우저 viewport 범위가 됩니다. 
  threshold: 1, // 타겟 엘리먼트(감시대상)가 감시범위(root요소)의 교차영역에 진입했을 때 ,타켓 엘리먼트의 100%가 감지될때 observe가 반응한다.
};

const observer = new IntersectionObserver(function (entries, observer) {
  console.log(entries)
  console.log(`변화가 감지되었습니다! 감지된 갯수:${entries.length}`);
  entries.forEach((entry) => {
    console.log(entry)
    
  });
}, options);

const childs = document.querySelectorAll("li");
childs.forEach((el) => observer.observe(el));

// 스크롤을 내리면서, observer 범위에서 사라지는것들은 intersection: false로 변함.

맨 마지막 줄을 통해 li 요소를 모두 감시등록 해주었습니다.

  • 초기화 동작
    감시등록을 하면, 맨처음 observer객체가 초기화될때, 등록한 모든 감시객체들의 observerEntry 객체를 entires배열에 넣어주고, 콜백을 실행하게됩니다.
    각 ObserverEntry객체를 뜯어보면, 현재 지정한 범위의 화면에 해당 요소가 보이는지의 상태(isInterSecting)를 확인할 수 있습니다.

  • 초기화 이후의 동작
    처음 초기화는 감시등록을 한 모든 li요소의 observerEntry 객체가 들어가있으므로, 갯수만큼 forEach를 돌게되나, 그 이후로는 , 상태변화(isInterSecting의 변화유무)에 따라, 변화가 감지된 요소의 observerEntry객체만 entries에 담겨서 콜백이 호출됩니다.

즉, 옵저버의 한번 초기화 이후 그 다음 콜백이 호출될때는, entries 배열에 변화가 발생한 entry객체만 담기게 됩니다.


사진으로 살펴보겠습니다.

1.observer.observe로 감시를 시작하고 처음 렌더링 됐을 때

li객체가 화면에 보이든(isInterSecting=true)안보이든 entries 에는 감시 대상이되는 모든 ObserverEntry객체를 담고있습니다.

1-1 각 ObserverEntry 객체를 살펴보자

이중에서는 우리의 관심사인 isIntersecting 속성을 확인 하였습니다.

2.화면을 스크롤해서 첫번째 요소를 살짝 가려보자.

threshold를 1로 지정했으므로, li 요소가 완전히 가려지거나, 보일때 콜백이 호출됩니다. 스크롤을 내려 아이템1을 살짝 가리자마자, 바로 entries배열에 담겨 콜백이 호출되는것을 확인할 수 있습니다.

현재는 아이템1의 교착상태만 변경되었으므로 , entries 배열에 ObserverEntry객체가 1개 들어온다는것을 확인할 수 있습니다.
isIntersecting 값 또한 true -> false로 변했구요.
참고로 , threshold는 1이므로 아이템3의 교착상태는 아직 감지되지 않습니다.

스크롤을 더 내려 아이템3을 보이도록 해보자.

만약 스크롤을 더 내려서 아이템3이 100% 보이게되면, 콜백이 호출되고, 아이템3의 ObserverEntry 객체가 entries로 들어갈 것입니다.

  • 주의할점
    다시 스크롤을 올려 아이템1을 보이도록하면,entries 배열에 아이템1 ObserverEntry 객체가 들어갑니다.
    따라서 한번이상 감지된 대상을 중복해서 감지하는 것을 제외하고싶다면, forEach문에서, Observer.unobserve() 를 통해 감시대상에서 제외시켜주어야 합니다.

스크롤이벤트를 달고, 스크롤위치를 계산하는 방법보다 훨씬 간단합니다. scroll계산을 위해 리플로우를 하지않기 때문에 렌더링에 장점이 있습니다. 다음에는 이를 이용해서 리액트에서 무한스크롤을 구현하는 코드를 살펴보겠습니다

좋은 웹페이지 즐겨찾기