IntersectionObserver

IntersectionObserver

프론트엔드 개발을 해오다보면 사용자의 스크롤에 따라 특정 조건을 감지해야하는 경우들이 정말 많다.

  • 컨텐츠의 최 하위 높이에 도달했을 경우 콜백 실행

    대표적으로 infinite scroll

  • 특정 높이에 되었을 때, 해당 컨텐츠에 속성 부여

    dataSet에 값을 갖고 있다가 조건이 되는 높이가 되었을 시, src속성 부여하는 lazy loading

위와 같은 경우, 대부분 수동으로 높이를 구해서 작업을 하였는데, 스크롤 이벤트 이후 실행되는 콜백메소드의 경우는 throttling방식으로 끊어줄 수 있지만, 해당 이벤트 자체는 계속 감지되는지라 불필요한 소요가 존재하기도 했다.

그러던 중 해당 API를 발견하였다.

MDN의 말을 내 느낌대로 줄여본다면, target이 되는 엘리먼트의 경계를 넘나들 때, 콜백함수가 실행되는것 같다.

예시

// dom
  <div class="div1 div"></div>
  <div class="div2 div"></div>

// script
  const options = {
    root: null
    rootMargin: "0px 0px 80px 0px",
    threshold: 0,
  };
  const io = new IntersectionObserver(entries => {
    entries.forEach(entry => {
      if (entry.intersectionRatio > 0) {
        // 모습을 드러냈으니, 원하는 로직 실행
        console.log(entry.target, "활성화");
      } else {
        // 모습을 감춘상태
        console.log(entry.target, "비활성화");
      }
    });
  }, options);

  const els = document.querySelectorAll(".div");
  els.forEach(el => io.observe(el));

해당 생성자에 있어서 첫번째 인자로는 콜백함수, 두번째 인자로는 옵션을 보내줄 수 있다.

옵션의 속성으로는

  1. root : 지정해 주지 않으면 기본값으로, 감지를 할 범위를 의미하는것 같다. 기본값은 window 전체인듯 하다.
  2. rootMargin : cssmargin과 같은 구조로, 감지를 하는 target요소 + margin값에 도달할 때, 콜백함수를 실행시킨다.
  3. threshold : 0~1사이의 값으로 보여지는 %를 의미한다고하는데, 사실상 대부분 보여지자마자 실행시키도록 하니깐 0으로 두면 될듯..?

콜백함수의 첫번째 변수에는 target이 하나의 요소든 여러개의 요소든 배열의 구조인것 같다. 따라서, forEach와 같은 순회 메소드로 관리하는것이 좋아보인다.

크게 신경쓸 속성으로는 target과, intersectionRatio가 있는것같다.

  1. target : 해당 API에 영향을 받는 요소들
  2. intersectionRatio : 해당 요소가 감지?되기위한 기준 으로 0보다 클경우 해당 요소가 조건에 해당되고, 0이면 해당되지 않는다.

예시 Infinite scroll

oberver 요소가 target이 되고, 해당 요소가 화면에 보일때 새로운 div를 추가해주는 방식이다.

unobserve 메소드를 통해, 첫번째 인자로 요소를 받으며, 요소에 대한 감지를 제거할 수 있다.

disconnect 메소드는 unobserve랑 비슷한 것 같은데, 아무런 요소도 받지 않는것 보아 전부를 해제하는 느낌이다.

// dom
    <div class="divWrap">
      <div class="div1 div"></div>
    </div>
    <div class="observer"></div>

// script
    let options = {
      rootMargin: "0px 0px 0px 0px",
      threshold: 0,
    };
    const divWrap = document.querySelector(".divWrap");
    const callback = entries => {
      entries.forEach((entry, index) => {
        if (entry.intersectionRatio > 0) {
          divWrap.innerHTML += `<div class="div${index + 2} div"></div>`;
        }
      });
    };
    let observer = new IntersectionObserver(callback, options);
    const target = document.querySelector(".observer");
    observer.observe(target);

    // observer.unobserve(target);
    // observer.disconnect();

주의점

감지 중단을 위해 unobserve를 사용한 다음, observe를 다시 호출해주게 될 경우, 스크롤이 끝에 가있을 때 무한히 호출된다.

당연한것이, observe상태일 때 대상이 지정한 비율만큼 보일 때 한번 실행되는데, 대상이 보이는 상태에서 unobserve시켰다가 다시 observe 하게 되면 실행되었다는 기록이 초기화된 상태이기 때문에 또 한번 실행되게되어 무한반복.

느낌

scroll이벤트의 경우 어쩔수없이 꾸준히 발생하고, 이후에 높이를 각각 계산하여야 했었는데, 모든것을 한번에 처리해주니 참 좋은듯 하다.

사용자가 스크롤을 사용할 때, 모든 스크롤을 감지하고 이벤트를 호출하게 되면서 발생하는 성능문제에 대해 throttling을 사용하는것이 권장되었지만, 특정 지점에 도달할 때 만 호출되도록 도와주는점이 큰 매력!

단, 비동기로 서버에 데이터를 요청하는것과는 별개인듯 하여 그 작업에 대해서는 별도의 성능개선작업을 해줘야 할 듯


@SangMin
👆 H'e'story

🚀GitHub