[JavaScript] 무한스크롤 구현하기

정다은·2022년 10월 26일
2
post-thumbnail

📌 참고
이 게시글은 [프로그래머스] 과제테스트 연습 - 고양이 사진 검색 사이트(https://school.programmers.co.kr/skill_check_assignments/4)를 풀어보며 공부한 내용의 일부를 정리하고 있습니다 :)


💡 요구사항

  • 스크롤 페이징 구현
    • 검색 결과 화면에서 유저가 브라우저 스크롤 바를 끝까지 이동시켰을 경우, 그 다음 페이지를 로딩하도록 만들어야 합니다.

✅ 스크롤 이벤트 핸들러 활용

📌 clientHeight / offsetHeight / scrollHeight 차이

  • clientHeight
    • 사용자에게 보여지는 (화면 영역의) element 높이 + 패딩
    • 패딩 포함 ⭕ 테두리/스크롤바/마진 포함 ❌
  • offsetHeight
    • 사용자에게 보여지는 element의 높이 + 패딩 + 테두리 + 스크롤바
    • 패딩/테두리/스크롤바 ⭕ 마진 ❌
  • scrollHeight
    • (보이지 않는 부분까지 포함한) element의 전체 높이 + 패딩 + 테두리 (CSS로 element의 높이를 지정할 때 정해지는 높이)
    • 패딩/테두리 ⭕ 마진 ❌

[참고] 요소 사이즈와 스크롤 | 모던 JavaScript 튜토리얼

🔽 스크롤바를 특정 요소 끝까지 이동시켰을 경우 다음 이벤트 실행하는 코드

const { clientHeight, scrollTop, scrollHeight } = e.target.scrollElement;
if (clientHeight + scrollTop >= scrollHeight) {
  /* 실행할 이벤트 */
}

// 또는
if (scrollHeight - scrollTop === element.clientHeight) {
  /* 실행할 이벤트 */
}

📌 window.innerHeight / window.scrollY

  • window.innerHeight
    • 브라우저에서 실제로 표시되고 있는 영역의 높이
  • window.scrollY
    • 스크롤이 Y축으로 얼마만큼 이동했는지 나타내는 값

🔽 스크롤바를 화면 끝까지 이동시켰을 경우 다음 이벤트 실행하는 코드

// document.body.scrollHeight: body(페이지 전체)의 높이를 나타냄
if (window.innerHeight + window.scrollY >= document.body.scrollHeight) {
  /* 실행할 이벤트 */
}

✅ Debounce 또는 Throttle 적용

  • DOM 이벤트 기반 실행되는 JavaScript의 이벤트를 성능 향상을 위해 제어하는 방법
  • Debounce(디바운스)
    • 연달아 호출되는 이벤트들을 그룹화하여 특정 시간이 지난 후에 가장 마지막으로 발생한 하나의 호출에 대한 이벤트만 실행되도록 하는 것
// 디바운싱 코드
let timer;
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
  /* 실행될 이벤트 */
}, 1000);

// 디바운싱을 적용한 스크롤 이벤트 핸들러
if (window.innerHeight + window.scrollY >= document.body.scrollHeight) {
  if (timer) clearTimeout(timer);
  timer = setTimeout(() => {
    console.log("End Of Document");
  }, 500);
}
  • Throttle (쓰로틀)
    • 마지막 이벤트가 호출된 후에는 일정한 시간이 지나기 전에 해당 이벤트가 다시 호출되지 않도록 하는 것
// 쓰로틀링 코드
let timer;
if (!timer) {
  	timer = setTimeout(() => {
      timer = null;
      /* 실행될 이벤트 */
    }, 1000);
}

// 쓰로틀링을 적용한 스크롤 이벤트 핸들러
if (window.innerHeight + window.scrollY >= document.body.scrollHeight) {
  if (!timer) {
    timer = setTimeout(() => {
      timer = null;
      console.log("End Of Document");
  }, 500);
}

✅ Intersection Observer API 활용

[참고] Intersection Observer API | MDN

  • 타겟 요소와 상위 요소 또는 최상위 document의 viewport 사이의 intersection 내의 변화를 비동기적으로 관찰하는 방법
  • 활용 사례
    • 페이지가 스크롤 되는 도중에 발생하는 이미지나 다른 컨텐츠의 지연 로딩
    • 스크롤 시에, 더 많은 컨텐츠가 로드 및 렌더링되어 사용자가 페이지를 이동하지 않아도 되게 하는 infinite-scroll 을 구현
    • 광고 수익을 계산하기 위한 용도로 광고의 가시성 보고
    • 사용자에게 결과가 표시되는 여부에 따라 작업이나 애니메이션을 수행할 지 여부를 결정
  • IntersectionObserver() 생성자
    • callback: 요소가 교차할 때 실행될 콜백함수
    • options
      • root: 타겟 교차할 요소 (기본값 null이자 브라우저 뷰포트)
      • rootMargin: root의 범위로 CSS의 margin 속성과 유사하게 "10px 20px 30px 40px" (top, right, bottom, left 순서)와 같이 지정 (기본값은 0)
      • threshold: 타겟 요소가 얼만큼 보여졌을 때 탐지하고 콜백을 실행할 것인지 지정하는 것으로, 50%를 원할 경우 0.5와 같이 지정하거나 25% 단위로 요소의 가시성이 변경될 때마다를 원할 경우 [0, 0.25, 0.5, 0.75, 1]과 같이 배열로 지정할 수 있음 (기본값은 0)
  • 콜백함수의 첫 번째 인자로 반환되는 IntersectionObserverEntry 객체
    • boundingClientRect, intersectionRatio, intersectionRect, isIntersecting, isVisible, rootBounds, target, time 속성 포함
// 무한 스크롤 예제
// 스크롤할 요소의 마지막에 빈 div를 하나 append한 후 해당 div를 observe한다
const $result = document.querySelector(".resultDiv");
const $end = document.createElement("div");
$result.appendChild($end);

const callback = (entries, observer) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      /* 실행될 이벤트 */
    }
  });
}

const observer = new IntersectionObserver(callback, option);
observer.observe($end);
profile
벨로그에는 주로 알고리즘 문제를 풀고 기록합니다 ✍

1개의 댓글

comment-user-thumbnail
2023년 7월 27일

좋은 글 감사합니다.

답글 달기