인터섹션 옵저버(IntersectionObserver)를 적용한 다양한 스크롤 이벤트 적용하기
인터섹션 옵저버(Intersection Observer)에 관해서 이전에 한번 정리했던 적이 있습니다.
우리말로 하면 "교차 관찰자" 정도로 정의될 거 같습니다. 인터섹션 옵저버가 무엇인지 모르거나 해당 요소를 좀 더 자세히 이해하고 싶으시면 이전 글을 한번 읽어보시고 다시 여기로 돌아와 공부를 하셔도 좋을 것 같습니다.
그냥 바로 이글을 읽기 원하시면 그냥 읽고 이해하시거나 이해가 안 되시는 부분이 있거나 추가적으로 의문이 생기실 때 읽으셔도 무방합니다. 그럼 지금부터 인터섹션 옵저버에 대해 좀 더 자세히 알아보고 실무에서 바로 사용할 수 있는 예제를 살펴보면서 인터섹션 옵저버를 언제 사용하는 건지 또 어떻게 사용하는 것인지 알아보도록 하겠습니다.
이전에 인터섹션 옵저버 API는 크롬 51버전부터 사용할 수 있으며 이 Web API는 2016년 4월 구글 개발자 페이지 통해 소개되었습니다.
특정 위치에 스크롤이 도달하였을 때 미리 정의해 둔 이벤트를 적용하려면 어떻게 해야 할까요?
인터섹션 옵저버 API가 없을 때에는 "scroll" 이벤트를 통해서 해당 이벤트 작업을 구현하였습니다. document에 스크롤 이벤트를 등록하고 특정 지점을 관찰하면서 스크롤이 해당 엘리먼트 위치에 도달하였을 때 콜백 함수를 등록하는 것이죠.
하지만 이 방법은 단 시간에 스크롤 이벤트가 매우 많이 호출되고 동기적으로 실행되며 한 페이지에 여러 엘리먼트에 이벤트가 등록되어 있는 경우에는 사용자가 스크롤을 할 때마다 스크롤을 감지하는 이벤트가 끊임없이 동작합니다.
이러한 부분들은 성능에 매우 안좋은 영향을 주게 됩니다.
이런 문제점들을 해결하기 위해 인터섹션 옵저버를 사용하여 구현하게 되면 리플로우 현상 문제점이 발생하지 않는걸 확인할 수 있습니다.
기본적인 사용법은 간단합니다.
인터섹션 옵저버를 선언하고(해당 요소에서 발생되어야 하는 이벤트) 선언이 끝나면 옵저버가 감시해야 할 요소를 적어주면 끝입니다.
// 인터섹션 옵저버 선언
const io = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.intersectionRatio > 0) {
entry.target.classList.add('animated');
}
})
});
// 감시 대상 선언, 감시(이벤트 발생)
const boxElList = document.querySelector('.box');
boxElList.forEach((el) => {
io.observe(el);
});
이처럼 편리한 인터섹션 옵저버이지만 한가지 단점이 있습니다.
바로 IE에서는 전혀 동작하지 않는다는 것입니다. 크로스 브라우징을 하면서 IE까지 모두 고려해야 하는 환경이라면 IE에서는 별도의 분기처리를 하거나 다른 방법으로 구현해야 한다는 번거로움이 있습니다.
그럼에도 불구하고 인터섹션 옵저버는 확실히 사용성에 따른 매력이 있는 스크립트입니다.
하지만 인터섹션 옵저버를 처음 접해본 분들은 제가 쉽다고 말씀드린 부분이 어렵다고 느끼실수도 있습니다.
또한 보다 많은 옵션값을 활용해서 인터섹션 옵저버를 보다 다양하게 사용하고 싶은 욕구가 있으신 분들도 있으실 거라 생각합니다.
그런 분들을 위해 좀 더 다양한 예제를 통해 상황에 맞게 사용하면 될 것 같습니다.
각 예제들의 최상단에 엘리먼트에 짝을 이룬 표시등을 두었고 콜백 함수가 호출될 때마다 파동 효과를 주었으며 이때 isIntersecting 속성을 기준으로 표시등을 켜지거나 꺼지게 되어 있습니다.
Intersection observer Demo
1. Default Intersection observer
2. Row Scroll : Intersection observer
3. Dynamic Area Control : Intersection observer
위의 예제들을 이해한다면 기본적인 인터섹션 옵저버를 사용할 준비가 되었습니다.
그러나 조금 더 세밀한 옵션값을 통한 이벤트를 조정하고 싶을 경우에는 API에서 제공하는 옵션을 활용하여 조금 더 다양하고 정밀하게 이벤트를 제어할 수 있습니다.
1. root
root 옵션에는 가시성의 판단 기준이 될 HTML 엘리먼트를 지정합니다. observe 메서드로 등록하는 엘리먼트들은 반드시 이 루트 엘리먼트의 자식이어야 합니다. 이 옵션을 지정하지 않을 경우 브라우저 화면에서 현재 보이는 영역인 뷰포트가 기본이 됩니다. 아래 예제에서 알 수 있듯이 루트 엘리먼트를 지정하면 현재 화면과는 상관없이 루트 엘리먼트와 등록한 엘리먼트들의 영역이 교차하는지 판단하게 됩니다.
2. rootMargin
루트 엘리먼트의 마진값을 지정할 수 있습니다. CSS에서 사용하는 형식과 같기 때문에 “10px”, “10px 20px”, “10px 20px 30px 40px” 형태가 모두 가능하며 음수 값으로 지정할 수도 있습니다. 기본값은 0이고 루트 엘리먼트를 지정한 상태라면 퍼센트 값을 사용할 수도 있습니다. 이 옵션을 이용하면 이미지 동적 로딩에서 해당 엘리먼트가 화면에 나타나기 전에 이미지를 불러오기 시작해 이미지 공백을 줄이는데 유용하게 이용할 수 있습니다.
다만 해당 예제는 iframe 내에서 실행을 하게 되면 이 옵션이 정상적으로 동작하지 않습니다.
이 예제는 아래 소스 코드를 복사하여 직접 로컬에서 확인해보면 정상적으로 동작함을 알 수 있습니다.
예제를 확인해보면 스크롤을 내리다보면 엘리먼트마다 IntersectionObserver 콜백 함수가 다른 위치에서 호출됨을 확인할 수 있습니다.
3. threshold
threshold 옵션은 엘리먼트가 콜백 함수의 호출 시점을 정하는 옵션입니다. 0과 1을 포함한 그 사이의 숫자 또는 숫자 배열을 지정할 수 있는데 이 숫자는 엘리먼트의 전체 영역 중에 현재 보이는 영역의 비율입니다. 이 비율의 경계를 넘나들 때마다 콜백 함수가 호출됩니다.
예제에서는 threshold 옵션을 각각 0, 0.5, 1, [0, 1]로 지정했습니다. rootMargin 예제처럼 엘리먼트마다 콜백 함수가 다른 위치에서 호출됩니다. 두 번째와 세 번째 상자는 콜백 호출 시점에 isIntersecting 값이 항상 참이기 때문에 표시등이 정상적으로 표시되지 않습니다. isIntersecting 속성을 기준으로 처리해야 할 작업이 있다면 반드시 threshold 속성에 0을 포함시켜야 정상적으로 동작합니다. threshold 속성은 아래 예제처럼 콜백 함수의 인자로 받는 IntersectionObserverEntry 객체의 intersectionRatio 속성과 같이 사용하기에 유용합니다.
인터섹션 옵저버를 활용하면 이미지 동적 로딩이나 무한 스크롤 기능도 구현 가능합니다.
무한 스크롤은 이전에 정리해 둔 인터섹션 옵저버(Intersection Observer) 글에서 확인할 수 있습니다.
모두들 이번에 배운 코드를 통해 좀 더 즐겁게 코딩하세요.
참고 :
https://tech.lezhin.com/2017/07/13/intersectionobserver-overview
http://blog.hyeyoonjung.com/2019/01/09/intersectionobserver-tutorial/