기본적으로 브라우저 뷰포트(Viewport)와 설정한 요소(Element)의 교차점을 관찰하며, 요소가 뷰포트에 포함되는지 포함되지 않는지, 사용자 화면에 현재 보이는 요소인지 아닌지를 구별하는 기능을 제공한다.
// ex.)
// ▼ 관찰자 초기화
const io = new IntersectionObserver(callback, options)
// ▼ 관찰할 대상(요소) 등록
io.observe(element)
callback
// ex.)
const io = new IntersectionObserver((entries, observer) => {}, options)
io.observe(element)
entries는 IntersectionObserverEntry 인스턴스의 배열이다.
IntersectionObserverEntry는 읽기 전용(Read only)의 다음 속성들을 포함한다. ▼
boundingClientRect: 관찰 대상의 사각형 정보(DOMRectReadOnly)
→ 포지션과 너비와 높이를 알 수 있다. 요소가 윈도우 창에 벗어나서 그려져있는 경우 x좌표는 0이지만, y좌표는 브라우저에서의 좌표 0.0 밖으로 넘어가 있기때문에 -
이다.
intersectionRect: 관찰 대상의 교차한 영역 정보(DOMRectReadOnly)
→ 얼마만큼 들어와 있는지 확인 할 수 있다. 0부터 1까지로 표현된다. (안에 전부 다 들어와 있다면 1 , 70%만 들어와 있다면 0.7)
intersectionRatio: 관찰 대상의 교차한 영역 백분율(intersectionRect 영역에서 boundingClientRect 영역까지 비율, Number)
→ 들어온 포지션과 크기가 들어있는 인터섹션이다.
isIntersecting: 관찰 대상의 교차 상태(Boolean)
→ 요소가 안으로 들어오는 상태라면 true
, 윈도우에서 밖으로 나가는 상태라면 false
가 된다.
rootBounds: 지정한 루트 요소의 사각형 정보(DOMRectReadOnly)
→ 아무것도 지정하지 않으면 (뷰포트 기준으로) 현재 보여지고 있는 윈도우창을 말한다.
target: 관찰 대상 요소(Element)
time: 변경이 발생한 시간 정보(DOMHighResTimeStamp)
const io = new IntersectionObserver((entries, observer) => {
console.log(observer)
}, options)
io.observe(element)
option
root : 설정하지 않으면 항상 null로 되어져 있다.
→ 따로 루트를 명시하지 않으면 뷰포트가 기본이다. (뷰포트: 우리가 보는 윈도우 부분)
→ 어떤 걸 기준으로 요소들이 들어오고 나가는지를 확인하고 싶을 때는, 부모 컨테이너를 지정해 줄 수 있다.
rootMargin : 바깥 여백(Margin)을 이용해 Root 범위를 확장하거나 축소할 수 있다.
→ 설정하지 않으면 항상 0픽셀로 되어져 있다.
→ 뷰포트가 있다고 가정하면 그 바깥으로 (윈도우에서 우리가 보이지않는 바깥부분) 지정한 값만큼 마진을 줘서, 마진까지 더 포함된 영역으로 계산하고자 할때 쓰인다.
→ 정해진 부모 컨테이너 이후부터 무언가를 미리 준비해 놓을 수 있다. (화면에 근접할 경우 미리 컨텐츠를 준비해놓을 때 유용하게 쓰임)
→ CSS의 margin과 같이 4단계로 여백을 설정할 수 있으며, px 또는 %로 나타낼 수 있다. 기본값은 0px 0px 0px 0px이며 단위를 꼭 입력해야 한다.
threshold : 얼마만큼 보여져야 콜백함수가 호출될지를 결정한다.
→ 기본값은 0이다. 즉, 1px만 보여도 콜백함수가 실행된다.
→ 전체 들어왔을 때 실행하고 싶으면 1로 설정, 0.5는 50%만 들어왔을 때 실행하게 된다.
→ 나갈 때는 (= isIntersecting 이 false일 때) 지정된 값의 반대가 된다. 즉 1이면 0, 0.2면 0.8
Methods
IntersectionObserver
인스턴스가 관찰하는 모든 요소의 관찰을 중지한다.IntersectionObserverEntry
객체의 배열을 반환한다.✍️ 코드를 작성해 가면서 이해하기
const callback = (entries, observer) => { // ③
console.log("observe!");
}
// 클래스의 생성자에 callback이라는 것을 전달해서, 새로운 observer라는 오브젝트를 만들었다
const observer = new IntersectionObserver(callback); // ②
const boxes = document.querySelectorAll(".box"); // ①
boxes.forEach((box) => observer.observe(box)); // ④
1) boxes
라는 변수에 document.querySelectorAll
를 이용해서 만들어둔 박스리스트를 할당한다.
2) 웹API에서 제공하는 IntersecrionObserver
라는 클래스를 이용해서 → new
연산자를 이용해서 오브젝트를 만든다. 이때 이 API는 콜백함수를 받는다.
3) 콜백함수를 정의한다. 그리고 이 콜백함수는 entries
와 observer
를 인자로 받을 수 있다고 한다. 이렇게 만들어진 관찰자를 이용해서 관찰한다.
4) 각각의 박스를 관찰자가 관찰하도록 명령해 놓은 상태이다.
entries
와 observer
라는 유용한 정보들을 전달해 준다고 이해했다.observe()
괄호 안에 어떤 대상이 들어가는지 궁금해서 MDN을 찾아보았다.▼ entries
와 observer
를 각각 출력하면 이렇게 뜬다.
▼ 처음에는 20개의 모든 아이템이 윈도우에 들어왔기 때문에 20개의 요소가 들어있는 것을 볼 수 있다
▼ 각각의 배열에는 인터섹션옵저버엔트리 라는 오브젝트가 들어있는 것을 볼 수 있다.
IntersectionObserverEntry
→ 화면에 들어온 요소에 대한 정보를 담음
entries는 배열이다.
const callback = (entries, observer) => {
// 배열이라서 >> [0]과 같은 인덱스 번호를 매기지 않고 출력했더니, undefined가 출력되었다.
console.log(entries[0].boundingClientRect); // [0]을 해주니 잘 출력!
};
▼ 궁금해서 출력해 보았다. 이런식으로 원하는 정보를 지정, 여러가지를 알 수 있을 것 같았다.
const callback = (entries, observer) => {
entries.forEach(entry1 => {
console.log(entry1.boundingClientRect);
})
};
▼ 한번 출력해보자
boundingClientRect
가 출력되는 것을 확인! 🤔forEach
작업을 해줌으로써, 각각의 배열요소마다 접근이 되기때문에 [0]과 같은 접근 없이 바로 boundingClientRect
에 접근 할 수 있었다.▼ 요소가 들어오고 나가는 것을 확인해보자
const callback = (entries, observer) => {
entries.forEach((entry1) => {
console.log(entry1.target);
});
};
❓들어오는 것과 나가는 것에 대해서 처리를 다르게 하고 싶다면
if
문 사용해보기isIntersecring
: 요소가 안으로 들어오는 상태라면 true, 윈도우에 있다가 밖으로 나가는 상태라면 false가 된다.const callback = (entries, observer) => {
entries.forEach((entry1) => {
if (entry1.isIntersecting) { // 들어오면 true 니까 true
console.log(entry1.target);
} else {
console.error(entry1.target); // 에러-> 빨간색으로 표시해서 구분해보기
}
});
};
✍️ 이렇게 들어오면 그대로, 나가면 에러가 출력되는 걸 구분해서 확인해 볼 수 있었다. 즉, 이런 방식으로 들어오고 나간 것에 대해서 원하는 처리를 할 수도 있을 것이다. 😀
+) 추가
✍️ 네비게이션 바를 일정 스크롤 위치에서 부터 sticky로 전환해서 보여주기
❗️ 스크롤 이벤트는 성능에 좋지않다.
const initialCoords = section1.getBoundingClientRect().top;
window.addEventListener('scroll', () => {
if (window.scrollY > initialCoords) nav.classList.add('sticky');
else nav.classList.remove('sticky');
});
const header = document.querySelector('.header');
const navHeight = nav.getBoundingClientRect().height;
const stickyNav = function (entries) {
const [entry] = entries; // entries는 IntersectionObserverEntry 인스턴스의 배열
console.log(entry);
// isIntersecting : 관찰 대상의 교차 상태(Boolean)
if (!entry.isIntersecting) nav.classList.add('sticky');
else nav.classList.remove('sticky');
};
const headerObserver = new IntersectionObserver(stickyNav, {
root: null, // 기본값 null -> 뷰포트가 기본
threshold: 0, // 기본값 0 -> 얼마만큼 보여져야 콜백을 호출할지 결정
rootMargin: `-${navHeight}px`, // 기본값 0px -> 마진을 이용해 Root 범위 확장 및 축소가능
});
headerObserver.observe(header);
reference
MDN - Intersection Observer API
MDN - IntersectionObserver.observe()
heropy - intersection _observer
dream-coding