IntersectionObserver(인터섹션 옵저버)
const io = new IntersectionObserver(callback, options) // 관찰자 초기화
io.observe(element) // 관찰할 대상(요소) 등록
Intersection observer는 기본적으로 브라우저 뷰포트(Viewport)와 설정한 요소(Element)의 교차점을 관찰하며, 요소가 뷰포트에 포함되는지 포함되지 않는지, 더 쉽게는 사용자 화면에 지금 보이는 요소인지 아닌지를 구별하는 기능을 제공합니다. 조금 더 쉽게 이해하면 특정 영역이 현재 유저가 바라보는 브라우저 화면에서 보이는 영역인지 아닌지를 구별할 수 있습니다. 이 기능은 비동기적으로 실행되기 때문에, scroll 같은 이벤트 기반의 요소 관찰에서 발생하는 성능 이슈 없이 사용할 수 있다는 장점이 있습니다.
IntersectionObserver의 사용 영역에 대한 정의는 아래와 같습니다.
- 페이지 스크롤 시 이미지를 Lazy-loading(지연 로딩)할 때
- Infinite scrolling(무한 스크롤)을 통해 스크롤할 때 새로운 콘텐츠를 불러올 때
- 광고의 수익을 계산하기 위해 광고의 가시성을 참고할 때
- 사용자가 결과를 볼 것인지에 따라 애니메이션 동작 여부를 결정할 때
new IntersectionObserver()를 통해 생성한 인스턴스(io)로 관찰자(Observer)를 초기화하고 관찰할 대상(element)을 지정합니다. 생성자는 2개의 인수(callback, options)를 가집니다.
const io = new IntersectionObserver(callback, options) // 관찰자 초기화
io.observe(element) // 관찰할 대상(요소) 등록
callback
관찰할 대상(Target)이 등록되거나 가시성(Visibility, 보이는지 보이지 않는지)에 변화가 생기면 관찰자는 콜백(Callback)을 실행합니다. 콜백은 2개의 인수(entries, observer)를 가집니다.
const io = new IntersectionObserver((entries, observer) => {}, options)
io.observe(element)
ㄱ) entries
entries는 IntersectionObserverEntry 인스턴스의 배열입니다.
IntersectionObserverEntry는 읽기 전용(Read only)의 다음 속성들을 포함합니다.
- boundingClientRect: 관찰 대상의 사각형 정보(DOMRectReadOnly)
- intersectionRect: 관찰 대상의 교차한 영역 정보(DOMRectReadOnly)
- intersectionRatio: 관찰 대상의 교차한 영역 백분율(intersectionRect 영역에서 boundingClientRect 영역까지 비율, Number)
- isIntersecting: 관찰 대상의 교차 상태(Boolean)
- rootBounds: 지정한 루트 요소의 사각형 정보(DOMRectReadOnly)
- target: 관찰 대상 요소(Element)
- time: 변경이 발생한 시간 정보(DOMHighResTimeStamp)
const io = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
console.log(entry) // entry is 'IntersectionObserverEntry'
})
}, options)
io.observe(element)
1. boundingClientRect
관찰 대상의 사각형 정보(DOMRectReadOnly)를 반환합니다.
이 값은, Element.getBoundingClientRect()를 사용해 동일하게 얻을 수 있습니다.(getBoundingClientRect 호출에서 Reflow 현상이 발생합니다.)
2. intersectionRect
관찰 대상과 루트 요소와의 교차하는(겹치는) 영역에 대한 사각형 정보(DOMRectReadOnly)를 반환합니다.
3. intersectionRatio
관찰 대상이 루트 요소와 얼마나 교차하는(겹치는)지의 수치를 0.0과 1.0 사이의 숫자로 반환합니다. 이는 intersectionRect 영역과 boundingClientRect 영역의 비율을 의미합니다.
4. isIntersecting
관찰 대상이 루트 요소와 교차 상태로 들어가거나(true) 교차 상태에서 나가는지(false) 여부를 나타내는 값(Boolean)입니다.
5. rootBounds
루트 요소에 대한 사각형 정보(DOMRectReadOnly)를 반환합니다. 이는 옵션 rootMargin에 의해 값이 변경되며, 만약 별도의 루트 요소(옵션 root)를 선언하지 않았을 경우 null을 반환합니다.
6. target
관찰 대상(Element)을 반환합니다.
7. time
문서가 작성된 시간을 기준으로 교차 상태 변경이 발생한 시간을 나타내는 DOMHighResTimeStamp를 반환합니다.
ㄴ) observer
콜백이 실행되는 해당 인스턴스를 참조합니다.
options
관찰이 시작하는 상황에 대하여 옵션을 설정할 수 있습니다. 기본 값이 정해져 있으므로 필수값은 아닙니다.
1. root
타겟의 가시성을 검사하기 위해 뷰포트 대신 사용할 요소 객체(루트 요소)를 지정합니다. 타깃의 조상 요소이어야 하며 지정하지 않거나 null일 경우 브라우저의 뷰포트가 기본 사용됩니다. 기본값은 null입니다.
const io = new IntersectionObserver(callback, {
root: document.getElementById('my-viewport')
})
2. rootMargin
바깥 여백(Margin)을 이용해 Root 범위를 확장하거나 축소할 수 있습니다. CSS의 margin과 같이 4단계로 여백을 설정할 수 있으며, px 또는 %로 나타낼 수 있습니다. 기본값은 0px 0px 0px 0px이며 단위를 꼭 입력해야 합니다.
- TOP, RIGHT, BOTTOM, LEFT / e.g. 10px 0px 30px 0px
- TOP, (LEFT, RIGHT), BOTTOM / e.g. 10px 0px 30px
- (TOP, BOTTOM), (LEFT, RIGHT) / e.g. 30px 0px
- (TOP, BOTTOM, LEFT, RIGHT) / e.g. 30px
const io = new IntersectionObserver(callback, {
rootMargin: '200px 0px'
})
3. threshold
옵저버가 실행되기 위해 타깃의 가시성이 얼마나 필요한지 백분율로 표시합니다. 기본값은 Array 타입의 [0]이지만 Number 타입의 단일 값으로도 작성할 수 있습니다. 0은 교차되지 않음을 1은 전체가 교차됨을 의미합니다.
- 0: 타겟의 가장자리 픽셀이 Root 범위를 교차하는 순간(타깃의 가시성이 0%일 때) 옵저버가 실행됩니다.
- 0.3: 타겟의 가시성 30%일 때 옵저버가 실행됩니다.
- [0, 0.3, 1]: 타겟의 가시성이 0%, 30%, 100%일 때 모두 옵저버가 실행됩니다.
const io = new IntersectionObserver(callback, {
threshold: 0.3 // or `threshold: [0.3]`
})
Methods
1. observe()
대상 요소의 관찰을 시작합니다.
const io1 = new IntersectionObserver(callback, options)
const io2 = new IntersectionObserver(callback, options)
const div = document.querySelector('div')
const li = document.querySelector('li')
const h2 = document.querySelector('h2')
io1.observe(div) // DIV 요소 관찰
io2.observe(li) // LI 요소 관찰
io2.observe(h2) // h2 요소 관찰
2. unobserve()
대상 요소의 관찰을 중지합니다. 관찰을 중지할 하나의 대상 요소를 인수로 지정해야 합니다.
단, IntersectionObserver 인스턴스가 관찰하고 있지 않은 대상 요소가 인수로 지정된 경우 아무런 동작도 하지 않습니다.
const io1 = new IntersectionObserver(callback, options)
const io2 = new IntersectionObserver(callback, options)
// ...
io1.observe(div)
io2.observe(li)
io2.observe(h2)
io1.unobserve(h2) // nothing..
io2.unobserve(h2) // H2 요소 관찰 중지
콜백의 두 번째 인수 observer가 해당 인스턴스를 참조하므로, 다음과 같이 작성할 수도 있습니다.
const io1 = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
// 가시성의 변화가 있으면 관찰 대상 전체에 대한 콜백이 실행되므로,
// 관찰 대상의 교차 상태가 false일(보이지 않는) 경우 실행하지 않음.
if (!entry.isIntersecting) {
return
}
// 관찰 대상의 교차 상태가 true일(보이는) 경우 실행.
// ...
// 위 실행을 처리하고(1회) 관찰 중지
observer.unobserve(entry.target)
})
}, options)
3. disconnect()
IntersectionObserver 인스턴스가 관찰하는 모든 요소의 관찰을 중지합니다.
const io1 = new IntersectionObserver(callback, options)
const io2 = new IntersectionObserver(callback, options)
// ...
io1.observe(div)
io2.observe(li)
io2.observe(h2)
io2.disconnect() // io2가 관찰하는 모든 요소(LI, H2) 관찰 중지
IE 지원(polyfill)
IntersectionObserver API를 지원하지 않는 브라우저(MSIE)에서도 사용할 수 있도록 공식적으로 라이브러리가 지원됩니다.
따로 설정할 것 없이 모듈을 가져오기만 하면 됩니다.
$ npm i intersection-observer
// For IE
import 'intersection-observer'
// 동일하게 사용하면 됩니다.
const io = new IntersectionObserver(callback, options)
const els = document.querySelectorAll('element')
// For IE
Array.prototype.slice.call(els).forEach(el => {
io.observe(el)
})
혹은 다음과 같이 전역으로 사용할 수도 있습니다.
<script src="https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserver"></script>
Infinite scroll