Intersection Observer API

katanazero·2020년 3월 22일
2

study-js

목록 보기
4/6
post-custom-banner

😀 Intersection Observer API ?

  • The Intersection Observer API provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document's viewport.
교차 관찰자 API(Intersection Observer API)는 상위요소 또는 최상위 문서의 뷰포트
(viewport)와 대상 요소의 교차점에서 변화를 비동기적으로 관찰할 수 있는 방법을 제공한다.
  • viewport ?
    display 요소가 표현되는 영역을 의미

  • viewport 영역에서 교차점에서 어떤 변화를 비동기적으로 관찰이 가능하게 도와줍니다.
  • 어떤 변화 또는 감지대상이 교차점에 진입했을 때 비동기 처리를 도와줍니다.

🥰 어디에 쓰면 좋나요?

  • Lazy-loading of images or other content as a page is scrolled.
  • Implementing "infinite scrolling" web sites, where more and more content is loaded and rendered as you scroll, so that the user doesn't have to flip through pages.
  • Reporting of visibility of advertisements in order to calculate ad revenues.
  • Deciding whether or not to perform tasks or animation processes based on whether or not the user will see the result.

MDN(https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API)에 4가지가 소개가 되어있습니다.

저는 주로, 지연로딩(lazy-loading)과 무한 스크롤링(infinite scrolling)을 위해 활용했던 경험이 있습니다.

예제

  • new IntersectionObserver(callback, { // options})
new IntersectionObserver(callback(entries, observer), {});

// 감지대상이 관찰이 되었을때, 실행할 함수다.
const callback = (entries, observer) => {
// callback 함수는 entries, observer 2개의 매개변수를 가진다.
// entries : 관찰대상 엘리먼트 배열
// observer : IntersectionObserver instance

}

const options = {
  root : null, // 변화를 관찰할 대상 요소
  rootMargin : '0px', // 대상 요소의 margin 값 설정
  thredhold : 0 // 관찰자가 반응할 교차점 백분율 (0 ~ 1 사이 값 지정)
}

// 교차점 백분율을 배열로 지정 가능
thredhold : [0.5, 1] // 50%, 100% 일때 감지

const observer = new IntersectionObserver(callback, options);
observer.observe(targetElement); // 이제 여기서, 어떤 엘리먼트들을 관찰할지 관찰자를 생성해줘야한다.

🤩 rootMargin 을 통해, 감시할 영역을 확장 또는 축소한다.

  • callback(entries, observer) {}
entries 는 배열이기 때문에, forEach 등으로 반복하여 요소에 접근가능.

entries[0].isIntersecting : root 영역에 교차되고 있는지 boolean 으로 반환(true, false)
entries[0].intersectionRatio : 0 ~ 1 사이로 실제 영역에 해당 엘리먼트가 표시되는 비율
entries[0].target : 현재 타겟 엘리먼트(event.target 과 동일)
entries[0].boundingClientRect : 해당 요소의 크기 및 위치좌표를 구함(viewport 기준 -> root 기준)

  • HTML
<section class="card-grid" id="target-root">
  <div class="card">1</div>
  <div class="card">2</div>
  <div class="card">3</div>
  <div class="card">난 언제 보이니? <br> 알려줘!!</div>
  <div class="card">5</div>
  <div class="card">6</div>
  <div class="card">7</div>
  <div class="card">8</div>
  <div class="card">9</div>
  <div class="card">10</div>
</section>
  • CSS
.card-grid {
  display : flex;
  flex-direction : column;
  align-items : center;
  
  width : 100%;
  height : 350px;
  border : 1px solid black;
  
  overflow : auto;
  
}

.card {
  display : flex;
  align-items : center;
  
  border : 1px dotted skyblue;
  width : 50%;
  height : 100px;
  min-height : 100px;
  margin : 20px;
    
}
  • JS
const callback = (entries, observer) => {
// callback 함수는 entries, observer 2개의 매개변수를 가진다.
console.log(entries);
}

const options = {
  root : document.getElementById('target-root'), // 변화를 관찰할 대상 요소
  rootMargin : '-40px', // 대상 요소의 margin 값 설정
  thredhold : 0.5 // 관찰자가 반응할 교차점 백분율 (0 ~ 1 사이 값 지정)
}

// 교차점 백분율을 배열로 지정 가능
// thredhold : [0.5, 1] // 50%, 100% 일때 감지

const observer = new IntersectionObserver(callback, options);
const targetElements = document.querySelectorAll('.card');
targetElements.forEach(target => observer.observe(target));

4번째 div 요소(index : 3) 은 대상요소에서 교차되지 않아
isIntersecting : false
intersectionRatio : 0
인걸 확인이 가능하다.

  • 위와 같이 사용하면, callback 이 스크롤을 움직일때마다 계속 호출이 되는데 뭔가 감지하여 작업을 완료하였다면 unobserve() 또는 disconnect() 메서드를 통해 관찰을 해제시켜주자!
const callback = (entries, observer) => {
  // callback 함수는 entries, observer 2개의 매개변수를 가진다.
  console.log(entries);

  entries.forEach(entry => {
    if (entry.isIntersecting) { // 감지대상이 교차영역에 진입 할 경우
      observer.unobserve(entry.target);
    }
  });


}

  • observer.disconnect(); -> 이거 호출하면 모든 관찰에 대해 중지한다.

😆 어때요? 참 좋은 API 아닌가요? addEventListener() 로 resize, scroll 이벤트를 구현할 필요가 없어요!

만약 scroll 이벤트 여러 엘리먼트에 생성했거나, 깜빡하고 removeEventListener() 해주지 않았다면..? 스크롤이 발생할때마다 event 가 열심히 감지되어 호출이 됩니다.
성능이 좋지 않겠죠? 😅

그리고 getBoundingClientRect() 함수같은 경우는 reflow 현상을 일으켜 이녀석도 성능에 영향을 미칩니다..

Reflow : 엘리먼트의 위치와 길이를 다시 계산하는 동작, 위치와 길이가 변경이 되어 영향을 받은 엘리먼트 수치도 같이 다 계산 -> 그리고 다시 랜더링 해줘야하니 랜더트리 다시 생성
-> 레이아웃, 크기, 위치

Repaint : Reflow 발생 이유와 같이 스타일의 모든 변경이 레이아웃 수치에 영향을 받는것은 아님 즉, 엘리먼트의 background-color, visibillty, outline 등의 스타일 변경 시에는 레이아웃 수치가 변경되지 않으므로 Reflow 과정이 생략된 Repaint 과정만 일어나게 된다.
-> 가시성, Reflow 발생으로 인한 사이드이펙트

profile
developer life started : 2016.06.07 ~ 흔한 Front-end 개발자
post-custom-banner

3개의 댓글

comment-user-thumbnail
2021년 1월 4일

명료하고 좋은 설명 너무나 감사합니다. 본문에 첨부해주신 예시 코드를 간단히 실행해볼 수 있도록 JSFiddle로 옮겨보았어요.
다른 분들께도 도움이 되었으면 좋겠네요~

1개의 답글