RoRo의 개선사항으로 Assessment Data 페이지에서 오른쪽 Contents 영역이 스크롤됨에 따라 왼쪽의 Title이 동적으로 선택되지 않는 현상을 현재 보고 있는 Contents와 선택된 Title이 일치하도록 개선하고자 하였습니다.
해당 기능을 구현하기 위해 생각해본 방법으로는 addEventListener()의 scroll 이벤트를 사용하여 구현하는 방법이 가장 먼저 떠올랐습니다. 하지만 구글링을 통해 TOC를 구현할 때 Intersection Observer API
를 사용해 구현할 수 있다는 것을 알게 되었습니다.
Intersection Observer API
가 새롭게 만들어진 이유로는 기존의 scroll 이벤트와 가시성 관찰에 사용되는 getBoundingClientRect() 메서드의 문제점 때문이었습니다.
scroll 이벤트는 단시간에 수백, 수천 번 호출되며 동기적으로 실행되기 때문에 main Thread에 영향을 미치게 됩니다. 그리고 특정 지점을 관찰하기 위해서는 getBoundingClientRect() 메서드를 사용해야 하는데 Reflow
현상이 발생한다는 성능상의 이슈가 있습니다.
Reflow: 레이아웃을 다시 계산하는 것으로, 브라우저가 웹 페이지의 일부 또는 전체를 다시 그려야 하는 경우 발생한다.
Intersection Observer
를 사용하면 비동기적으로 실행되기 때문에 Main Thread에 영향을 주지 않고 reflow를 발생시키지 않으면서 변경 사항을 관찰할 수 있기 때문에 성능상 유리해질 수 있습니다.
Intersection Observer API
는 2016년 4월 구글 개발자 페이지를 통해 소개되었습니다.
타겟 요소와 최상위 document의 viewport 사이의 intersection
내의 변화를 비동기적으로 관찰하는 방법으로, 지정한 target이 화면에 노출되었는지에 대한 여부를 간단하게 구독할 수 있는 API 입니다.
MDN에서는 Intersection Observer
의 필요성을 아래와 같은 예를 들어 설명하고 있습니다.
const observer = new IntersectionObserver(callback, options);
new 키워드를 통해 인스턴스를 생성하고 2개의 파라미터로 callback, option을 받습니다.
callback
let callback = (entries, observer) => {
entries.forEach(entry => {
// Each entry describes an intersection change for one observed
// target element:
// entry.boundingClientRect (관찰 대상의 사각형 정보)
// entry.intersectionRatio (관찰 대상의 교차한 영역 정보)
// entry.intersectionRect (관찰 대상의 교차한 영역 백분율)
// entry.isIntersecting (관찰 대상의 교차 상태, Boolean)
// entry.rootBounds (지정한 루트 요소의 사각형 정보)
// entry.target (관찰 대상 요소)
// entry.time (변경이 발생한 시간 정보)
});
};
타겟 요소의 관찰이 시작되거나, 가시성 변화가 감지되면 호출되는 콜백입니다.
파라미터로 entries와 observer를 받게 됩니다.
options
let options = {
root: document.getElementById('json-table'),
rootMargin: '0px',
threshold: [0],
}
타겟 요소의 가시성을 확인할 때 사용되는 루트 요소입니다. 타겟 요소보다 상위 요소여야 하며, 설정하지 않거나 null일 시 default로 브라우저 viewport가 설정됩니다.
margin
을 주어 루트 요소의 범위를 확장할 수 있습니다. CSS의 margin같이 사용할 수 있으며 단위(px, %)를 꼭 입력해야 합니다.
callback이 실행될 타겟 요소의 가시성 범위를 설정할 수 있습니다. 0.0부터 1.0 사이의 숫자 혹은 배열로, target에 대한 교차 영역 비율을 설정할 수 있습니다.
IntersectionObserver.observe()
target에 대한 IntersectionObserver를 등록(관찰)합니다.
IntersectionObserver.unobserve()
target 요소에 대한 관찰을 중지합니다.
IntersectionObserver.disconnect()
다수의 target 엘리먼트를 관찰하고 있을 때, 모든 요소들에 대한 관찰을 중지합니다.
IntersectionObserver.takerecords()
IntersectionObserverEntry
객체의 배열을 리턴합니다.
TOC를 구현하게 되며 새롭게 Intersection Observer 라는 API에 대해 알게 되어 유용하게 사용해볼 수 있었습니다.
기존에 사용하고 있던 scroll 이벤트를 IntersectionObserver를 활용해 대체할 수 있다면 성능을 개선할 수 있고, 웹페이지에 걸리게 되는 광고 컨텐츠 등에 대해서도 다양히 활용할 수 있을 거 같습니다.
Ref:
https://developer.mozilla.org/ko/docs/Web/API/Intersection_Observer_API