스크롤이 끝날 때까지 작업 지연

웹 기능: scrollend

웹에서 스크롤은 부드럽고 반응성이 좋아야 합니다. 스크롤 중에 레이아웃 재계산, 분석 데이터 비콘 추적, 동적 DOM 업데이트와 같은 과중한 작업을 실행하면 기본 스레드가 포화 상태가 되어 프레임 드롭 및 레이아웃 스래싱이 발생할 수 있습니다.

지금까지 개발자는 setTimeout()를 사용하여 scroll 이벤트를 디바운싱하여 스크롤이 완료되는 시점을 추측했습니다. 하지만 이러한 디바운스 함수는 신뢰할 수 없는 것으로 알려져 있습니다. 사용자가 스크롤하는 동안 트리거될 수 있습니다.

scrollend 이벤트는 신뢰성이 높고 실적 중심의 솔루션을 제공합니다. 브라우저는 스크롤이 멈추고 모든 전환이 완료되고 터치 동작이 해제되는 정확한 시점에 scrollend 이벤트를 실행합니다.

구현 방법

작업 지연 패턴을 구현하려면 다음 단계를 따르세요.

  1. 스크롤 가능한 컨테이너 설정: overflow: auto 또는 overflow: scroll를 사용하여 컨테이너를 만듭니다.
  2. scroll 이벤트 수신 대기: 기본 동적 측정항목 또는 정보 레이아웃 스타일 지정에만 이 리스너를 사용합니다. 여기에서 과중한 작업을 실행하지 마세요.
  3. scrollend 이벤트 수신 대기: 스크롤 컨테이너 또는 문서 자체에 scrollend 콜백을 등록합니다.
  4. 콜백에서 비용이 많이 드는 작업 실행: 동적 콘텐츠를 가져오거나 포괄적인 DOM 레이아웃을 트리거하는 것이 안전한 위치입니다.

예시 코드

.scroll-container {
  height: 300px;
  overflow-y: auto;
}
const scroller = document.querySelector('.scroll-container');

// 1. Informative feedback during scroll
scroller.addEventListener('scroll', () => {
  // Avoid dynamic heavier data updates here
  console.log('Scrolling dynamically... updates deferred');
});

// 2. Safe callback when scrolling rests
scroller.addEventListener('scrollend', () => {
  // Run layout recalculations or analytical beacons updates here
  const currentVisibleSection = findMostVisibleSection(scroller);
  fetchAdditionalData(currentVisibleSection);
});

전략적 구현 및 권장사항

  • 레이아웃 데이터 비콘을 실행하거나 새 콘텐츠 레이아웃을 동적으로 가져올 때는 디바운스된 scroll 이벤트 대신 scrollend를 사용합니다.
  • 캐러셀 또는 추천 갤러리 슬라이드를 빌드하는 경우 scrollSnapChange 또는 scrollSnapChanging 스냅 상호작용과 페어링하는 것이 좋습니다.
  • 동적 시각적 스크롤 콜백 내에 레이아웃 종속 동적 업데이트를 번들로 묶지 않습니다.
  • 권장사항: 시각적 표시 영역 확대/축소 및 스크롤이 scrollend 이벤트를 올바르게 트리거하는지 확인합니다.

대체 전략

지원되지 않는 브라우저의 경우 setTimeout를 사용하여 디바운스된 scroll 이벤트로 대체하여 커스텀 scrollend 이벤트를 전달합니다.

function initializeDemo() {
  const scroller = document.querySelector('#scroller');
  scroller.addEventListener('scrollend', () => {
    // Safe execution
  });
}

if ('onscrollend' in window) {
  initializeDemo();
} else {
  initializeDemo();
  const scroller = document.querySelector('#scroller');
  scroller.addEventListener('scroll', () => {
    clearTimeout(window.scrollendtimer);
    window.scrollendtimer = setTimeout(() => {
      scroller.dispatchEvent(new CustomEvent('scrollend'));
    }, 100);
  });
}