1번 상황
서비스하고 있는 사이트에 이미지가 2200개가 있다고 가정하겠습니다.
웹의 로드와 동시에 2200개의 이미지를 전부다 받아와야 한다면 사용자는 기다리는 동안 커피를 사올수 있고 세차도 가능할 지도 모릅니다.
2번 상황
3500개의 DOM 객체가 있고 이들이 화면에 나타나면 색깔이 변해야 한다는 기획이 있다고 가정해 보겠습니다.
css의 animation 속성을 이용해 색깔을 계속 바꿔지도록 할 수도 있어서 화면에 나타나고 딱 한번만 색깔이 바뀌어야 한다고 조건을 추가하겠습니다.
scroll 이벤트를 걸고 모든 DOM 객체들의 getBoundingClientRect를 이용하여 뷰포트 내의 위치를 가져오고 이를 이용하여 클래스를 변경해주는 방식도 있을 수 있을것 같습니다.
getBoundingClientRect는 뷰포트 에서의 위치를 가져올 수 있지만 레이아웃이 발생하게 됩니다. 정확한 위치를 제공해주고 싶은 브라우저의 열망으로 인해 리플로우가 발생하게 되는 것이죠.
이러면 사용자는 풋살 한경기를 뛰고 올 수도 있을 겁니다.
사실 방법은 많지만 여기선 interSectionObserver API를 소개하려 합니다.
(위의 if를 읽고 과장이 너무 심하다 싶으시면 너그럽게 용서해주십쇼 ㅎㅎ)
1번은 loading="lazy" 속성을 사용하면 효율적으로 해결할 수 있고 interSectionObserver API를 사용해도 됩니다.
(loading="lazy" 사용할때 처음에 뷰포트내에 보이는 이미지들은 loading 속성 사용하지 않는게 좋습니다. 브라우저에서 내부적으로 interSectionObserver를 통해 확인하고 load 하여서 사용하지 않는것보다 진짜 완전 조금 더 오래 걸릴수 있다 하네요 )
실제 src를 img의 data-src요소에 넣어두고 observer로 관찰하다가 콜백 내에서 해당 img의 src를 바꿔주는 방식이 있을것 같습니다.
2번은 interSectionObserver API 외에는 딱히 떠오르지 않네요
Intersection Observer API는 교차 관찰자라는 뜻으로서 타겟 요소의 뷰포트와 타겟 엘리먼트의 교차점을 관찰하고 타겟이 뷰포트 내에 들어왔는지 아닌지를 파악 + 그 외의 정보를 제공해주는 기능을 합니다.
공식문서에 따르면 아래와 같은 경우 사용할 수 있다고 합니다.
- 페이지가 스크롤 되는 도중에 발생하는 이미지나 다른 컨텐츠의 지연 로딩
- 스크롤 시에, 더 많은 컨텐츠가 로드 및 렌더링되어 사용자가 페이지를 이동하지 않아도 되게 하는 infinite-scroll을 구현
- 광고 수익을 계산하기 위한 용도로 광고의 가시성 보고
- 사용자에게 결과가 표시되는 여부에 따라 작업이나 애니메이션을 수행할 지 여부를 결정
간단한 코드 사용 예시를 아래에 추가하였습니다.
눈으로 한번 읽히신 후에 아래의 항목별 설명을 참조하는 것이 좋을것 같습니다.
let options = {
root: document.querySelector("#root"),
rootMargin: '0px',
threshold: [0, 0.3, 1]
}
let callback = (entries, obserer) => {
entries.forEach(entry => {
// target element:
// entry.boundingClientRect
// entry.intersectionRatio
// entry.intersectionRect
// entry.isIntersecting
// entry.rootBounds
// entry.target
// entry.time
})
}
let io = new IntersectionObserver(callback, options);
const boxList = document.querySelectorAll(".box")
boxList.forEach(el => {
io.observe(el)
})
const firstBox = document.querySelector(".box")
io.unobserver(firstBox)
io.disconnect()
IntersectionObserver의 두번째 인자인 options 객체 입니다.
let options = {
root: document.querySelector("#root"),
rootMargin: '0px',
threshold: [0, 0.3, 1]
}
let io = new IntersectionObserver(callback, options);
root => 뷰포트 대신 사용할 루트 요소를 설정할 수 있습니다.
설정해주지 않을경우 기본값은 뷰포트가 됩니다.
(어떠한 경우에 root 요소를 설정해서 사용할지 궁금하네요)
rootMargin => margin을 설정해줌으로써 Root 범위를 확장해주거나 축소할 수 있습니다.
threshold => 실행되기 위한 가시성 기준을 명시할 수 있습니다. 위의 예시는 0%, 30%, 100% 일때 콜백을 실행하겠다는 뜻이 됩니다.
콜백 함수를 등록해주는 부분입니다.
제공해주는 정보가 많은데 어떠한 정보를 제공해주는지 아래에서 확인할 수 있습니다.
let callback = (entries, obserer) => {
entries.forEach(entry => {
// target element:
// entry.boundingClientRect
// entry.intersectionRatio
// entry.intersectionRect
// entry.isIntersecting
// entry.rootBounds
// entry.target
// entry.time
})
}
let io = new IntersectionObserver(callback, options);
boundingClientRect => 타겟 요소의 getBoundingClientRect와 같은 정보를 제공해 줍니다.
intersectionRatio => 관찰 대상과 루트 요소가 얼마나 겹치는지 비율을 반환해 줍니다. 0 ~ 1 사이의 값을 가집니다.
intersectionRect => 관찰 대상과 교차하는 영역에 대한 사각형 정보를 반환합니다.
isIntersecting => 교차가 이루어졌는지 Boolean 값을 제공해 줍니다.
rootBounds => 루트 요소에 대한 정보를 제공해 줍니다.
target => 관찰 대상을 반환합니다.
time => 교차가 이루어진 시간을 반환합니다.
const boxList = document.querySelectorAll(".box")
boxList.forEach(el => {
io.observe(el)
})
const firstBox = document.querySelector(".box")
io.unobserve(firstBox)
io.disconnect()
observer 메서드 입니다.
이제 해당 메서드들의 관찰을 시작할 것이고 options의 설정에 따라 callback이 동작하게 될 것입니다.
unobserver 메서드 입니다.
관찰을 하고 있던 요소의 관찰을 중지하겠다는 뜻입니다.
target을 인자로 전달하여야 합니다.
disconnect 메서드 입니다.
관찰을 하고 있던 모든 요소에 대한 관찰을 중지하겠다는 뜻입니다.
이 기능을 지원하지 않는 브라우저에서는 사용하기 위해 폴리필을 추가해야 합니다.
라이브러리가 추가되었다고 하네요
https://github.com/w3c/IntersectionObserver/tree/main/polyfill
npm i intersection-observer
import "intersection-observer"
const io = new IntersectionObserver(callback, options)
const els = document.querySelectorAll("element")
Array.prototype.slice.call(els).forEach(el => {
io.observe(el)
})
// els는 유사배열 객체여서 Array 메서드를 가지고 있지 않습니다.
// Array처럼 순회하기 위해 Array.prototype.slice 메서드에 call을 통해 binding을 하여
// observe를 하나씩 붙여준 것을 확인할 수 있습니다.
출처: http://blog.hyeyoonjung.com/2019/01/09/intersectionobserver-tutorial/
https://fe-developers.kakaoent.com/2021/211202-gpu-intersection-observer/
https://heropy.blog/2019/10/27/intersection-observer/
https://developer.mozilla.org/ko/docs/Web/API/Intersection_Observer_API
'Frontend > Javascript' 카테고리의 다른 글
Javascript Don't Use Javascript Variables Without knowing Temporal Dead zone (번역글 + 생각) 💀 (0) | 2022.04.24 |
---|---|
Javascript undefined/undeclared/null and NaN 🧟 (0) | 2022.04.23 |
Javascript async/await, Promise 실수 포인트 🗝 (0) | 2022.04.19 |
Javascript 콜백 📞 (0) | 2022.04.19 |
Javascript Promise, async/await MicroTask queue 동작차이 🖇 (1) | 2022.04.15 |