강디너의 개발 일지

화면에 특정 DOM 이 보이는가? - InterSectionObserver API 본문

Javascript/삽질

화면에 특정 DOM 이 보이는가? - InterSectionObserver API

강디너 2020. 9. 3. 01:41
728x90

InterSectionObserver 란 (developer.mozilla.org/ko/docs/Web/API/Intersection_Observer_API)

Intersection Observer API는 타겟 요소와 상위 요소 또는 최상위 document의viewport 사이의 intersection 내의 변화를 비동기적으로 관찰하는 방법입니다.

 

intersection 정보는 아래와 같은 여러 가지 이유 때문에 필요합니다:

  • 페이지가 스크롤되는 도중에 발생하는 이미지나 다른 콘텐츠의 지연 로딩.
  • 스크롤 시에, 더 많은 콘텐츠가 로드 및 렌더링 되어 사용자가 페이지를 이동하지 않아도 되게 하는 infinite-scroll을 구현.
  • 광고 수익을 계산하기 위한 용도로 광고의 가시성 보고.
  • 사용자에게 결과가 표시되는 여부에 따라 작업이나 애니메이션을 수행할지 여부를 결정.

이전까지는 스크롤 이벤트를 이용해서 특정 높이에 다 달았을 때 함수를 실행시켰다면, InterSectionObserver API를 이용해서 특정 DOM이 노출했을 경우에 함수를 실행시킬 수 있습니다. 

 

 

IE 지원

InterSectionObserver API를 지원하지 않는 브라우저도 사용할 수 있도록 polyfill도 있습니다.

아래 코드를 넣으면 IE에서도 사용할 수 있습니다.

<script src="https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserver"></script>

사용법

const callback = (entries, observe) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
    	console.log('노출');
    }
  }
};

const observer = new IntersectionObserver(callback, options);

callback - 2가지 파라미터를 가진 콜백 함수

  • entries: IntersectionObserver를 적용한 객체들을 배열로 반환합니다. forEach를 통해서 각각의 객체로 접근합니다. 
  • observer: IntersectionObserver를 보여줍니다.(적용된 옵션들)

7개를 설정해서 entries 가 7개

 

options - 3가지 옵션을 가집니다.

  • root: 대상 객체의 가시성을 확인할 때 사용되는 뷰포트 요소입니다. 이는 대상 객체의 조상 요소여야 합니다. 기본값은 브라우저 뷰포트이며, root 값이 null 이거나 지정되지 않을 때 기본값으로 설정됩니다.
  • rootMargin: root 가 가진 여백이며 기본 값은 0입니다.
  • threshold: 대상 요소의 가시성 %를 나타내며 Number, Array 형태로 설정할 수 있습니다. 만약 50% 가 보였을 때 콜백 함수를 호출하고 싶으면 0.5로 설정하며 되며, 요소의 25%가 보일 때마다 호출하고 싶은 경우 [0, 0.25, 0.5, 0.75, 1]과 같이 Array 형태로 설정하면 됩니다.

methods

IntersectionObserver.obsserve(target) - 관찰 시작

IntersectionObserver.unobserve(target) - 관찰 중지

IntersectionObserver.disconnect() - 모든 관찰 종료

등등...


예시

HTML

<div class="default">
        <ul class="box-area">
            <li id="0" class="default-box">0</li>
            <li id="1" class="default-box">1</li>
            <li id="2" class="default-box">2</li>
            <li id="3" class="default-box">3</li>
            <li id="4" class="default-box">4</li>
            <li id="5" class="default-box">5</li>
            <li id="6" class="default-box">6</li>
        </ul>
        <ul class="nav-area">
            <li class="default-list">0</li>
            <li class="default-list">1</li>
            <li class="default-list">2</li>
            <li class="default-list">3</li>
            <li class="default-list">4</li>
            <li class="default-list">5</li>
            <li class="default-list">6</li>
        </ul>
</div>

 

1. 노출하면 바로 로그에 찍기

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (!entry.isIntersecting) {
      document.querySelectorAll('.default-list')[entry.target.id].classList.remove('active');
      return;
    }
    console.log('노출하는 상자 ID: ',entry.target.id);
    // 노출되어있는 상자 ID값을 이용하여 active 클래스 적용
    document.querySelectorAll('.default-list')[entry.target.id].classList.add('active');
  });
});

document.getElementsByClassName('default-box').forEach((box) => {
	observer.observe(box);
});

 

2. options에 margin 설정

options에 root, rootMargin을 설정했습니다. (root를 설정해야만 rootMargin이 먹습니다.)

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (!entry.isIntersecting) {
      document.querySelectorAll('.default-list')[entry.target.id].classList.remove('active');
      return;
    }
    console.log('노출하는 상자 ID: ',entry.target.id);
    document.querySelectorAll('.default-list')[entry.target.id].classList.add('active');
    });
}, { root: document.getElementsByClassName('default')[0], rootMargin: '20px'});

document.getElementsByClassName('default-box').forEach((box) => {
	observer.observe(box);
});

설정한 rootMargin 만큼 안 보여도 노출되었다고 확인할 수 있습니다.

3. options에 thredhold 설정

thredhold를 설정 후 특정 비율이 노출되면 콜백 함수를 호출할 수 있습니다.

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (!entry.isIntersecting) {
      document.querySelectorAll('.default-list')[entry.target.id].classList.remove('active');
      return;
    }
    console.log('노출하는 상자 ID: ',entry.target.id);
    document.querySelectorAll('.default-list')[entry.target.id].classList.add('active');
    });
  }, { root: document.getElementsByClassName('default')[0], threshold: 0.5});

document.getElementsByClassName('default-box').forEach((box) => {
	observer.observe(box);
});

50% 가 노출되면 콜백 함수를 부르는 것을 볼 수 있습니다.


위의 API를 공부하게 된 계기가 하나의 질문에서 시작되었습니다.

특정 상품이 정해진 %만큼 노출되면 API를 한번 쏘고 싶은데 어떻게 해야 할까요?

예를 들어 광고 상품인 경우에 상품 하나 노출되면 광고주는 돈을 내야 하는 예민한 사항입니다.

스크롤 이벤트 같은 경우 화면 사이즈에 영향을 많이 받기 때문에 이런 상황에서는 스크롤 이벤트로 불안정하게 할 수 없습니다.

 

하지만 IntersectionObserver API를 사용한다면 원하는 조건을 다 맞출 수 있을 것 같아서 공부하게 되었습니다.

 

 

노출이 되면 한 번만 API 쏘도록 하는 것은  unobserve 사용하면 된다고 알려드리면 될 것 같습니다.

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (!entry.isIntersecting) {
      document.querySelectorAll('.default-list')[entry.target.id].classList.remove('active');
      return;
    }
    console.log('노출하는 상자 ID: ', entry.target.id);
    observer.unobserve(entry.target);
    document.querySelectorAll('.default-list')[entry.target.id].classList.add('active');
  });
}, { root: document.getElementsByClassName('default')[0], threshold: 0.5});

 

* 추가

저희 신상마켓 소개 페이지IntersectionObserver API 를 이용한 라이브러리를 사용했습니다.

특정 DOM이 보이면 애니메이션이 시작하는 방식입니다.

 

참고 문헌

레진 기술 블로그

Intersection Observer로 무한 스크롤 구현하기

IntersectionObserver API

Intersection Observer - 요소의 가시성 관찰

Intersection Oserver API - MDN

 

긴 글을 읽어 주셔서 감사합니다.

잘못된 정보가 있으면 알려주시면 감사하겠습니다.

커피한잔...
Comments