Intersection Observer API란
Intersection Observer API는 타겟 요소와 상위 요소 또는 최상위 document 의 viewport 사이의 intersection 내의 변화를 비동기적으로 관찰하는 방법입니다.
- MDN
화면 상의 Target Element
노출 여부를 감지하는 API
이다.
스크롤 이벤트 감지 방법과의 비교
- scroll event를 감지하여 구현한다. 이때
throttle
혹은debounce
과 같은 처리가 추가로 필요하며throttle
/debounce
를 사용하는 경우 쓰레드 메모리를 차지하고 성능에도 좋지않다. scrollTop
+window.clientHeight
등을 사용해서 끝에 도달했는지 계산한다. 이때clientHeight
이나offsetTop
같은 엘리먼트의 위치를 가져와서 계산해야 하는 값은 브라우저가 정확한 값을 구하기 위해 렌더링 큐에 쌓인 모든 작업을 수행하면서Reflow
를 발생시킬 수 있다.
Intersection Observer Syntax
new IntersectionObserver (callback[, options]);
callback 함수는 다음과 같이 2개의 매개변수를 받는다.
entries
👉 더 드러나거나 가려지면서 지정한 역치를 넘어가게 된 요소를 나타내는IntersectionObserverEntry
객체의 배열observer
👉 자신을 호출한IntersectionObserver
자세한 설명은 mdn 참고
IntersectionObserver() - Web API | MDN
IntersectionObserver() 생성자는 새로운 IntersectionObserver 객체를 생성하고 반환합니다.
developer.mozilla.org
무한 스크롤 구현
간단하게 구현하고자 하는 것은 아래와 같다.
- 리스트 목록 보여주기 - 스크롤 시 리스트 끝에 도달하면 새로운 리스트 추가
먼저, 리스트 목록을 보여줄 엘리먼트(ul
)와 Intersection Observer의 target 엘리먼트(div
)를 추가했다.
<ul id="list"></ul> <div id="observer"></div>
다음으로, 리스트 목록을 추가하는 함수를 구현했다. 한 번 호출시마다 li
를 10개씩 추가한다.
let count = 0; const $ul = document.getElementById('list'); function makeListElement() { Array(10) .fill(0) .forEach(() => { const $li = document.createElement('li'); $li.textContent = ++count; $ul.appendChild($li); }); }
마지막으로, 무한스크롤을 구현하는 부분이다. Intersection Observer의 타겟 엘리먼트가 화면에 보여질 때(isIntersecting
) 리스트의 목록을 추가했다. 이때 API 호출시 생기는 딜레마를 표현하기 위해서 1초의 timeout을 지정했다.
let timer; const $observer = document.getElementById('observer'); const io = new IntersectionObserver((entries) => { clearTimeout(timer); if (entries[0].isIntersecting) { timer = setTimeout(() => makeListElement(), 1000); } }); io.observe($observer); // observer에 target element 등록
즉, 무한스크롤이 일어나는 과정은 다음과 같다.
ul
태그 밑에 있는 target Element가 화면에 노출된다. (= 리스트의 끝에 도달했다)- 1초의 딜레마 후 리스트에 10개의 엘리먼트가 추가된다.
ul
태그에 자식 엘리먼트가 추가되면서 화면에는 새로 추가된 자식 엘리먼트가 보여진다. target Element 부분은 리스트의 맨 밑으로 이동하여 화면에 보여지지 않게 된다.- 다시 스크롤을 통해 맨 밑으로 내리면(=리스트에 끝에 도달하면) target Element가 화면에 노출되면서 리스트를 추가한다.
전체코드
전체코드는 다음과 같다. 실행결과는 Demo에서 확인해볼 수 있다.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Infinite Scroll</title> <style> ul { list-style-type: none; display: flex; flex-direction: column; align-items: center; padding: 0; } li { background: #fff3bf; border-radius: 2px; margin: 1rem; padding: 20% 0; text-align: center; font-size: 100px; width: 80%; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24); transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); } li:nth-child(2n) { background-color: #c3fae8; } li:hover { box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22); } #observer { width: 50px; height: 50px; margin: 30px auto; border: 5px solid rgba(0, 0, 0, 0.12); border-radius: 50%; border-top-color: #c3fae8; animation: spin 1s ease-in-out infinite; -webkit-animation: spin 1s ease-in-out infinite; } @keyframes spin { to { -webkit-transform: rotate(360deg); } } @-webkit-keyframes spin { to { -webkit-transform: rotate(360deg); } } </style> </head> <body> <ul id="list"></ul> <div id="observer"></div> <script> let count = 0; const $ul = document.getElementById('list'); function makeListElement() { Array(10) .fill(0) .forEach(() => { const $li = document.createElement('li'); $li.textContent = ++count; $ul.appendChild($li); }); } makeListElement(); let timer; const $observer = document.getElementById('observer'); const io = new IntersectionObserver((entries) => { clearTimeout(timer); if (entries[0].isIntersecting) { timer = setTimeout(() => makeListElement(), 1000); } }); io.observe($observer); </script> </body> </html>
'Frontend' 카테고리의 다른 글
React-Query 정리 (0) | 2022.08.05 |
---|---|
[Javascript] Throttle 과 Debounce (0) | 2022.05.12 |
[Redux] 간단한 예제로 살펴보는 리덕스의 동작 원리 (0) | 2022.03.17 |
[React] ContextAPI & useContext Hook을 통한 Global State 값 관리하기 (0) | 2022.03.11 |
[Javascript] 변수, 호이스팅, TDZ (0) | 2022.03.07 |