우아한 테크 캠프 2차 과제를 대비하면서 프로그래머스에서 과제 테스트를 연습하고 있었는데
무한 스크롤과 레이지 로딩 구현이 나오길래 연습 겸 기록을 남긴다
scroll 이벤트
단순하게 scroll 이벤트로도 레이지 로딩과 무한 스크롤이 구현 가능하다
하지만 이 방법은 두 가지 문제가 존재하는데
- 스크롤 이벤트를 써 본 사람이라면 알겠지만 휠을 한 번 내릴 때 마다 콘솔창에 무수히 많이 찍히는 이벤트 로그들..
그 로그들을 봤다면 이게 성능에 영향을 준다는 것을 직감적으로 알 수 있을 것이다 - getBoundingClientRect()을 사용해서 특정 엘리먼트의 위치를 파악하려 할 때 DOM전체를 다시 그리는 리플로우가 발생해서 심각한 성능 저하를 가져온다
그래서 나온 것이 IntersectionObserve이다
IntersectionObserve API
IntersectionObserve은 위에서 나온 문제를 겪지 않고 매우 간단하게 특정 엘리먼트의 위치를 파악하고, 원하는 동작을 실행할 수 있게 해준다
IE가 지원을 안 하긴 하지만 나는 신경 쓸 필요가 없을 것 같다
처음에 이 API를 소개하는데 개념만 쭉 읊어놨길래 당최 뭔소린지 하나도 모르겠다가 4번 째 블로그를 정독할 때 쯤 감이 잡히기 시작했다
역시 여러 사람의 글을 다양하게 읽어보는게 답인듯
어떻게 실행되는지 부터 확인하자
See the Pen QWpEjLB by unganam (@unganam) on CodePen.
스크롤을 위아래로 내리면서 확인해보자
내가 보고있는 화면안에 item이 들어오면 item박스가 커지고 화면 밖으로 나가면 작아지는게 전부인 간단한 일을 하고있다
const option = {};
const io = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('change');
return
}
entry.target.classList.remove('change');
});
}, option);
[...document.querySelectorAll('.item')].forEach(item=>io.observe(item));
자바스크립트 코드는 위와 같다
우선 제일 첫 줄에 있는 option은 무시해도 좋다 지금은 사용하지 않고있으니까 실제로 값을 주지 않아도 잘 작동한다
1. [...document.querySelectorAll('.item')].forEach(item=>io.observe(item));
이해를 돕기 위해 마지막 줄 부터 먼저 확인해볼텐데
document.querySelectorAll 로 가져온 엘리먼트들은 배열로 작동하는 것 처럼 보이지만 실제로는 유사 배열이다
이대로 사용해도 상관없지만 spread문법을 이용해서 가져온 아이템들을 배열로 만들어주었다
배열로 만들어 준 후에 forEach을 이용해 io.observe(item)으로 가져온 아이템들을 지켜보고 있다 라고 생각하면 편하다
무엇을 하고 있는지는 이따 알아보자
2. new IntersectionObserve()
IntersectionObserve객체를 하나 만드는 것이다
이 객체를 만드는데는 콜백 함수 하나와 옵션이 하나씩 필요하다
옵션은 위에서 처럼 값을 주지 않아도 잘 돌아가기 때문에 나중에 알아보고
(entries, observer) => {
}
콜백 함수는 이러한 형태를 띠고 있다
여기서 파라미터로 받는 entries는 1번에서 쳐다보고 있는 아이템들이 entries로 온다
observer는 자기 자신
entries.forEach()
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('change');
return
}
entry.target.classList.remove('change');
});
entries.forEach로 반복문을 돌린다
isIntersecting은 내가 쳐다보고 있는 아이템이 화면 안에 들어왔는지 여부를 boolean값으로 알려준다
코드를 풀어보면 아이템이 내가 보고 있는 화면 안에 들어왔는지 확인하고 그 값이 true라면 아이템에 change클래스를 주고
그 값이 false라면 change클래스를 삭제한다
option
옵션은 총 3가지 설정값을 갖는다
- root : 화면 지정, 어느 화면에 들어왔을 때 동작을 실행시킬 것인지 화면을 지정하는 것이다 기본값은 내가 보는 화면, view-port
- rootMargin : 말 그대로 여백값을 주는건데 루트에서 지정한 화면 넓이보다 크기를 키울 수 있다 기본값은 0px 0px 0px 0px
- threshold : 어느 정도 화면에 들어왔을 때 동작을 실행시킬 것인지 정하는 값 클수록 화면에 많이 들어와야하며 기본값은 0
레이지 로딩 예제
See the Pen JjWKYeW by unganam (@unganam) on CodePen.
const option = {};
const io = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.src = entry.target.dataset.src;
observer.unobserve(entry.target);
}
});
}, option);
[...document.querySelectorAll('.item')].forEach(item=>io.observe(item));
달라진 점은 entry에 이미지 src를 전달해주고 나서 unobserve로 지켜보는 것을 그만두었다는 것
이미 이미지를 불러왔기 때문에 더 이상 그 엘리먼트를 관찰할 필요가 없기 때문
rootMargin 옵션도 줘서 예를 들고 싶었는데 코드펜에서는 적용이 되지 않아서 빼버렸다
'JavaScript' 카테고리의 다른 글
바닐라 자바스크립트로 컴포넌트 만들기 -1 (0) | 2021.05.17 |
---|---|
IntersectionObserve API, 무한 스크롤 (0) | 2021.05.15 |
소수, 실수 계산 오류, 부동소숫점 (0) | 2021.04.16 |
Vanilla JS로 MVC 패턴 구현하기 1 (0) | 2021.03.04 |
async/await (0) | 2021.02.07 |