Now You See Me: IntersectionObserver를 사용하여 지연, 지연 로드 및 조치하는 방법

게시 됨: 2022-03-10
빠른 요약 ↬ 이미지 로딩 지연 등 여러 가지 이유로 교차로 정보가 필요합니다. 하지만 더 많은 것이 있습니다. Intersection Observer API에 대해 더 나은 이해와 다양한 관점을 얻을 때입니다. 준비가 된?

옛날 옛적에 사이트가 모든 브라우저에서 동일하게 표시되어서는 안 된다고 고객에게 성공적으로 설득하고 접근성에 신경을 썼으며 CSS 그리드의 얼리 어답터였던 웹 개발자가 살았습니다. 그러나 그의 마음 속 깊은 곳에서 그의 진정한 열정은 성능이었습니다. 그는 지속적으로 최적화, 축소, 모니터링하고 그의 프로젝트에서 심리적 트릭을 사용하기까지 했습니다.

그러던 어느 날, 그는 사용자에게 즉시 표시되지 않고 화면에 의미 있는 콘텐츠를 렌더링하는 데 필수적이지 않은 지연 로딩 이미지 및 기타 자산에 대해 배웠습니다. 새벽의 시작이었습니다. 개발자는 지연 로딩 jQuery 플러그인의 악의 세계에 들어섰습니다(또는 어쩌면 그다지 사악하지 않은 asyncdefer 속성의 세계). 어떤 사람들은 그가 모든 악의 핵심인 scroll 이벤트 리스너의 세계에 곧바로 들어갔다고 말하기까지 합니다. 그가 어디로 갔는지 확실히 알 수는 없지만 이 개발자는 절대 가상의 인물이며 모든 개발자와의 유사점은 단지 우연의 일치일 뿐입니다.

웹 개발자
가상의 웹 개발자

글쎄, 이제 판도라의 상자가 열렸고 가상의 개발자가 문제를 덜 사실적으로 만들지 않는다고 말할 수 있습니다. 요즘에는 스크롤 없이 볼 수 있는 콘텐츠의 우선 순위를 지정하는 것이 속도 및 페이지 가중치 관점에서 웹 프로젝트의 성능에 매우 중요해졌습니다.

점프 후 더! 아래에서 계속 읽기 ↓

이 기사에서 우리는 scroll 의 어둠에서 벗어나 리소스를 지연 로드하는 현대적인 방법에 대해 이야기할 것입니다. 이미지를 지연 로드하는 것뿐만 아니라 해당 문제에 대한 모든 자산을 로드합니다. 더욱이, 오늘 우리가 이야기할 기술은 자산을 지연 로드하는 것 이상을 할 수 있습니다. 사용자에게 요소의 가시성을 기반으로 모든 유형의 지연된 기능을 제공할 수 있습니다.

IntersectionObserver: Now You See Me

신사 숙녀 여러분, Intersection Observer API에 대해 이야기해 보겠습니다. 그러나 시작하기 전에 IntersectionObserver 로 이끈 최신 도구의 환경을 살펴보겠습니다.

2017년은 우리 브라우저에 내장된 도구에 있어 아주 좋은 해였습니다. 많은 노력을 들이지 않고도 코드 기반의 스타일과 품질을 개선하는 데 도움이 되었습니다. 오늘날 웹은 매우 다른 기반의 산발적인 솔루션에서 Observer 인터페이스(또는 그냥 "Observers")의 보다 잘 정의된 접근 방식으로 매우 일반적인 해결 방식으로 이동하는 것 같습니다. 잘 지원되는 MutationObserver는 새로운 제품군을 얻었습니다. 최신 브라우저에서 채택:

  • IntersectionObserver 및
  • PerformanceObserver(성능 타임라인 레벨 2 사양의 일부로).

또 한 명의 잠재적인 가족 구성원인 FetchObserver는 진행 중인 작업이며 우리를 네트워크 프록시의 영역으로 더 많이 안내하지만 오늘은 대신 프론트엔드에 대해 더 많이 이야기하고 싶습니다.

IntersectionObserver 및 PerformanceObserver는 Observers 제품군의 새로운 구성원입니다.
IntersectionObserver 및 PerformanceObserver는 Observers 제품군의 새로운 구성원입니다.

PerformanceObserverIntersectionObserver 는 프론트 엔드 개발자가 서로 다른 지점에서 프로젝트의 성능을 개선하도록 돕는 것을 목표로 합니다. 전자는 실제 사용자 모니터링을 위한 도구를 제공하고 후자는 실질적인 성능 향상을 제공하는 도구입니다. 이전에 언급했듯이 이 기사에서는 후자인 IntersectionObserver 에 대해 정확히 자세히 살펴볼 것입니다. 특히 IntersectionObserver 의 역학을 이해하려면 일반 Observer가 현대 웹에서 어떻게 작동해야 하는지 살펴봐야 합니다.

전문가 팁 : 이론을 건너뛰고 IntersectionObserver의 역학을 바로 살펴보거나, 더 나아가 IntersectionObserver 의 가능한 응용 프로그램으로 바로 이동할 수 있습니다.

관찰자 대 이벤트

"관찰자"는 이름에서 알 수 있듯이 페이지의 컨텍스트에서 발생하는 것을 관찰하기 위한 것입니다. 관찰자는 DOM 변경과 같이 페이지에서 일어나는 일을 볼 수 있습니다. 또한 페이지의 수명 주기 이벤트를 볼 수도 있습니다. 관찰자는 또한 일부 콜백 기능을 실행할 수 있습니다. 이제 주의 깊은 독자는 즉시 여기에서 문제를 발견하고 "요점이 무엇입니까? 이를 위한 이벤트가 이미 있지 않습니까? 옵저버는 무엇이 다른가요?” 아주 좋은 점! 자세히 살펴보고 정리해보자.

관찰자 대 이벤트: 차이점은 무엇입니까?
관찰자 대 이벤트: 차이점은 무엇입니까?

일반 이벤트와 관찰자의 결정적인 차이점은 기본적으로 전자는 이벤트가 발생할 때마다 동기적으로 반응하여 메인 스레드의 응답성에 영향을 미치는 반면 후자는 성능에 그다지 영향을 주지 않고 비동기적으로 반응해야 한다는 것입니다. 적어도 이것은 현재 제시된 Observers에 대해 사실입니다. 그들 모두는 비동기적으로 행동 하며, 저는 이것이 미래에 변경되지 않을 것이라고 생각합니다.

이것은 초보자를 혼란스럽게 할 수 있는 관찰자의 콜백을 처리하는 주요 차이점으로 이어집니다. 관찰자의 비동기 특성으로 인해 여러 관찰 가능 항목이 동시에 콜백 함수에 전달될 수 있습니다. 이 때문에 콜백 함수는 단일 항목이 아니라 항목의 Array 에 항목이 하나만 포함되더라도).

게다가, 일부 Observers(특히 오늘 우리가 이야기하는 것)는 매우 편리한 사전 계산된 속성을 제공합니다. 그렇지 않으면 일반 이벤트를 사용할 때 값비싼(성능 관점에서) 메서드와 속성을 사용하여 스스로를 계산하곤 했습니다. 이 점을 명확히 하기 위해 기사 뒷부분에서 예제를 살펴보겠습니다.

따라서 누군가가 이벤트 패러다임에서 한 발짝 물러나기가 어렵다면 관찰자는 스테로이드의 이벤트라고 말하고 싶습니다. 또 다른 설명은 다음과 같습니다. 관찰자는 이벤트에 대한 새로운 수준의 근사값입니다. 그러나 어떤 정의를 선호하든 상관없이 Observer가 이벤트를 대체할 의도가 없다는 것은 말할 필요도 없습니다(적어도 아직까지는 아님). 둘 다에 대한 충분한 사용 사례가 있으며 나란히 행복하게 살 수 있습니다.

관찰자는 이벤트를 대체할 수 없습니다. 둘 다 함께 행복하게 살 수 있습니다.
관찰자는 이벤트를 대체할 수 없습니다. 둘 다 함께 행복하게 살 수 있습니다.

일반 관찰자의 구조

Observer의 일반 구조(작성 당시 사용 가능한 모든 것)는 다음과 유사합니다.

 /** * Typical Observer's registration */ let observer = new YOUR-TYPE-OF-OBSERVER(function (entries) { // entries: Array of observed elements entries.forEach(entry => { // Here we can do something with each particular entry }); }); // Now we should tell our Observer what to observe observer.observe(WHAT-TO-OBSERVE);

다시 말하지만, entries 은 단일 항목이 아니라 값의 Array 입니다.

이것은 일반적인 구조입니다. 특정 Observers의 구현은 관찰자 observe() 에 전달되는 인수와 콜백에 전달되는 인수가 다릅니다. 예를 들어 MutationObserver 는 또한 관찰할 DOM의 변경 사항에 대해 더 많이 알기 위해 구성 객체를 가져와야 합니다. PerformanceObserver 는 DOM의 노드를 관찰하지 않지만 대신 관찰할 수 있는 전용 항목 유형 집합이 있습니다.

여기에서 이 토론의 "일반적인" 부분을 끝내고 오늘 기사의 주제인 IntersectionObserver 에 대해 더 자세히 살펴보겠습니다.

IntersectionObserver 분해하기

IntersectionObserver 분해하기
IntersectionObserver 분해하기

먼저 IntersectionObserver 가 무엇인지 알아보겠습니다.

MDN에 따르면:

Intersection Observer API는 대상 요소와 상위 요소 또는 최상위 문서의 뷰포트와의 교차점에서 변경 사항을 비동기식으로 관찰하는 방법을 제공합니다.

간단히 말해서 IntersectionObserver 는 한 요소가 다른 요소와 겹치는 것을 비동기적으로 관찰합니다. IntersectionObserver 에서 이러한 요소가 무엇을 위한 것인지 이야기해 보겠습니다.

IntersectionObserver 초기화

이전 단락 중 하나에서 일반 Observer의 구조를 보았습니다. IntersectionObserver 는 이 구조를 약간 확장합니다. 우선, 이러한 유형의 Observer에는 세 가지 주요 요소가 포함된 구성이 필요합니다.

  • root : 관찰에 사용되는 루트 요소입니다. 관찰 가능한 요소에 대한 기본 "캡처 프레임"을 정의합니다. 기본적으로 root 는 브라우저의 뷰포트 이지만 실제로 DOM의 모든 요소가 될 수 있습니다(그런 다음 rootdocument.getElementById('your-element') 와 같은 것으로 설정함). 관찰하려는 요소는 이 경우 root 의 DOM 트리에 "살아 있어야" 합니다.
IntersectionObserver 구성의 루트 속성
root 속성은 요소의 '캡처 프레임'에 대한 기반을 정의합니다.
  • rootMargin : root 의 치수가 충분한 유연성을 제공하지 않을 때 "캡처 프레임"을 확장 하거나 축소 하는 root 요소 주변의 여백을 정의합니다. 이 구성의 값에 대한 옵션은 rootMargin: '50px 20px 10px 40px' (상단, 오른쪽 하단, 왼쪽)과 같은 CSS의 margin 옵션과 유사합니다. 값은 약식일 수 있으며(예 rootMargin: '50px' ) px 또는 % 로 표현할 수 있습니다. 기본적으로 rootMargin: '0px' .
IntersectionObserver 구성의 rootMargin 속성
rootMargin 속성은 root 로 정의된 '캡처 프레임'을 확장/축소합니다.
  • threshold : 관찰된 요소가 "캡처 프레임"( rootrootMargin 의 조합으로 정의됨)의 경계와 교차할 때 항상 즉시 반응하기를 원하지는 않습니다. threshold 은 관찰자가 반응해야 하는 교차의 백분율을 정의합니다. 단일 값 또는 값 배열로 정의할 수 있습니다. threshold 의 효과를 더 잘 이해하기 위해(때로는 혼란스러울 수 있음을 압니다) 다음은 몇 가지 예입니다.
    • threshold: 0 : 관찰된 요소의 맨 처음 또는 맨 마지막 픽셀이 "캡처 프레임"의 경계 중 하나와 교차할 때 기본값인 IntersectionObserver 가 반응해야 합니다. IntersectionObserver 는 방향에 구애받지 않습니다. 즉, a) 요소 가 들어갈 때와 b) "캡처 프레임"을 떠날 때 두 시나리오에서 모두 반응합니다.
    • threshold: 0.5 : 관찰된 요소의 50%가 "캡처 프레임"과 교차할 때 관찰자가 시작되어야 합니다.
    • threshold: [0, 0.2, 0.5, 1] ​​: 관찰자는 4가지 경우에 반응해야 합니다.
      • 관찰된 요소의 맨 처음 픽셀이 "캡처 프레임"에 들어갑니다. 요소가 여전히 실제로 해당 프레임 내에 있지 않거나 관찰된 요소의 맨 마지막 픽셀이 "캡처 프레임"을 떠납니다. 요소가 더 이상 프레임 내에 있지 않습니다.
      • 요소의 20%는 "캡처 프레임" 내에 있습니다(다시 말하지만 방향은 IntersectionObserver 의 경우 중요하지 않습니다).
      • 요소의 50%가 "캡처 프레임" 내에 있습니다.
      • 요소의 100%가 "캡처 프레임" 내에 있습니다. 이것은 threshold: 0 과 완전히 반대입니다.
IntersectionObserver 구성의 임계값 속성
threshold 속성은 Observer가 실행되기 전에 요소가 '캡처 프레임'과 얼마나 교차해야 하는지를 정의합니다.

IntersectionObserver 에 원하는 구성을 알리기 위해 다음과 같이 콜백 함수와 함께 config 개체를 Observer의 생성자에 전달하기만 하면 됩니다.

 const config = { root: null, // avoiding 'root' or setting it to 'null' sets it to default value: viewport rootMargin: '0px', threshold: 0.5 }; let observer = new IntersectionObserver(function(entries) { … }, config);

이제 IntersectionObserver 에 관찰할 실제 요소를 제공해야 합니다. 이것은 단순히 요소를 observe() 함수에 전달하여 수행됩니다.

 … const img = document.getElementById('image-to-observe'); observer.observe(image);

이 관찰된 요소에 대해 주의해야 할 몇 가지 사항:

  • 이전에 언급했지만 다시 언급할 가치가 있습니다. root 를 DOM의 요소로 설정하는 경우 관찰된 요소는 root 의 DOM 트리 내에 있어야 합니다.
  • IntersectionObserver 는 한 번에 하나의 관찰 요소만 허용할 수 있으며 관찰에 대한 일괄 공급을 지원하지 않습니다. 즉, 여러 요소(한 페이지에 여러 이미지가 있다고 가정해 봅시다)를 관찰해야 하는 경우 모든 요소를 ​​반복하고 각각을 개별적으로 관찰해야 합니다.
 … const images = document.querySelectorAll('img'); images.forEach(image => { observer.observe(image); });
  • Observer가 있는 페이지를 로드할 때 IntersectionObserver 의 콜백이 관찰된 모든 요소에 대해 한 번에 실행되었음을 알 수 있습니다. 제공된 구성과 일치하지 않는 경우에도 마찬가지입니다. "음… 기대했던 것과는 다릅니다." 이것을 처음 경험할 때 흔히 하는 생각입니다. 그러나 여기서 혼동하지 마십시오. 이것이 관찰된 요소가 페이지가 로드되는 동안 "캡처 프레임"과 어떻게든 교차한다는 의미는 아닙니다.
모든 요소에 대해 한 번에 실행되는 IntersectionObserver가 있는 DevTools의 스크린샷.
IntersectionObserver 는 일단 등록되면 모든 관찰된 요소에 대해 실행되지만 이것이 모두 우리의 '캡처 프레임'과 교차한다는 의미는 아닙니다.

하지만 이것이 의미하는 바는 이 요소에 대한 항목이 초기화되었고 이제 IntersectionObserver 에 의해 제어된다는 것입니다. 이것은 콜백 함수에 불필요한 노이즈를 추가할 수 있으며 실제로 "캡처 프레임"과 교차하는 요소와 여전히 설명할 필요가 없는 요소를 감지하는 것은 사용자의 책임이 됩니다. 이러한 감지를 수행하는 방법을 이해하기 위해 콜백 함수의 구조를 좀 더 자세히 살펴보고 이러한 항목이 무엇으로 구성되어 있는지 살펴보겠습니다.

IntersectionObserver 콜백

먼저 IntersectionObserver 의 콜백 함수는 두 개의 인수를 취하며 두 번째 인수부터 역순으로 설명하겠습니다. 앞서 언급한 관찰된 항목 Array 과 함께 "캡처 프레임"과 교차하는 콜백 함수는 관찰자 자체두 번째 인수로 가져옵니다.

관찰자 자체에 대한 참조

 new IntersectionObserver(function(entries, SELF) {…});

Observer 자체에 대한 참조를 가져오는 것은 IntersectionObserver 에서 처음으로 감지한 일부 요소의 관찰을 중지하려는 경우 많은 시나리오에서 유용합니다. 이미지의 지연 로드, 다른 자산의 지연된 가져오기 등과 같은 시나리오가 이러한 유형입니다. 요소 관찰을 중지하려는 경우 IntersectionObserver 는 관찰된 요소에 대해 일부 작업(예: 이미지의 실제 지연 로딩과 같은)을 수행한 후 콜백 함수에서 실행할 수 있는 unobserve(element-to-stop-observing) 메서드를 제공합니다. ).

이러한 시나리오 중 일부는 기사에서 더 자세히 검토하겠지만 이 두 번째 인수는 생략하고 이 콜백 플레이의 주요 행위자에 대해 알아보겠습니다.

IntersectionObserverEntry

 new IntersectionObserver(function(ENTRIES, self) {…});

콜백 함수에서 Array 로 얻는 entries 은 특수 유형인 IntersectionObserverEntry 입니다. 이 인터페이스는 관찰된 각 요소와 관련하여 미리 정의되고 미리 계산된 속성 집합을 제공합니다. 가장 흥미로운 것을 살펴 보겠습니다.

우선 IntersectionObserverEntry 유형의 항목에는 프로세스와 관련된 요소의 좌표와 경계를 정의하는 세 가지 다른 직사각형에 대한 정보가 함께 제공됩니다.

  • rootBounds : "캡처 프레임"에 대한 직사각형( root + rootMargin );
  • boundingClientRect : 관찰된 요소 자체에 대한 직사각형.
  • intersectionRect : 관찰된 요소와 교차하는 "캡처 프레임"의 영역입니다.
IntersectionObserverEntry의 직사각형
IntersectionObserverEntry와 관련된 모든 경계 사각형이 자동으로 계산됩니다.

비동기식으로 계산되는 이러한 사각형의 정말 멋진 점은 getBoundingClientRect() , offsetTop , offsetLeft 및 레이아웃 스래싱을 ​​트리거하는 기타 값비싼 위치 지정 속성 및 메서드를 호출하지 않고도 요소의 위치 지정과 관련된 중요한 정보를 제공한다는 것입니다. 성능에 대한 순수한 승리!

흥미로운 IntersectionObserverEntry 인터페이스의 또 다른 속성은 isIntersecting 입니다. 관찰된 요소가 현재 "캡처 프레임"과 교차하는지 여부를 나타내는 편의 속성입니다. 물론, intersectionRect 를 보고 이 정보를 얻을 수 있지만(이 사각형이 0×0이 아니면 요소가 "캡처 프레임"과 교차하고 있음) 이를 미리 계산하면 매우 편리합니다.

isIntersecting 은 관찰된 요소가 "캡처 프레임"에 막 들어가고 있는지 아니면 이미 떠나고 있는지 확인하는 데 사용할 수 있습니다. 이것을 찾으려면 이 속성의 값을 전역 플래그로 저장하고 이 요소에 대한 새 항목이 콜백 함수에 도착하면 새 isIntersecting 을 해당 전역 플래그와 비교하십시오.

  • 그것이 false 이었고 지금은 true 요소가 "캡처 프레임"에 들어가는 것입니다.
  • 그 반대이고 이전에는 true 이었지만 지금은 false 이면 요소가 "캡처 프레임"을 떠나는 것입니다.

isIntersecting 은 바로 앞에서 논의한 문제를 해결하는 데 도움이 되는 속성입니다. 즉, "캡처 프레임"을 실제로 교차하는 요소에 대한 항목을 항목의 초기화에 불과한 항목의 노이즈와 분리하는 것입니다.

 let isLeaving = false; let observer = new IntersectionObserver(function(entries) { entries.forEach(entry => { if (entry.isIntersecting) { // we are ENTERING the "capturing frame". Set the flag. isLeaving = true; // Do something with entering entry } else if (isLeaving) { // we are EXITING the "capturing frame" isLeaving = false; // Do something with exiting entry } }); }, config);

참고 : Microsoft Edge 15에서는 isIntersecting 속성이 구현되지 않았으며 그렇지 않은 경우 IntersectionObserver 에 대한 전체 지원에도 불구하고 undefined 를 반환합니다. 이것은 2017년 7월에 수정되었으며 Edge 16부터 사용할 수 있습니다.

IntersectionObserverEntry 인터페이스는 하나 이상의 사전 계산된 편의 속성인 intersectionRatio 를 제공합니다. 이 매개변수는 isIntersecting 과 같은 목적으로 사용될 수 있지만 부울 값 대신 부동 소수점 숫자이기 때문에 더 세분화된 제어를 제공합니다. intersectionRatio 값은 관찰된 요소의 영역이 "캡처 프레임"( intersectionRect 영역 대 boundingClientRect 영역의 비율)과 교차하는 정도를 나타냅니다. 다시 말하지만, 우리는 이 직사각형의 정보를 사용하여 스스로 계산할 수 있지만 우리를 위해 수행하는 것이 좋습니다.

벌써부터 낯익지 않나요? 예, <code>intersectionRatio</code> 속성은 Observer 구성의 <code>threshold</code> 속성과 유사합니다. 차이점은 후자는 Observer를 실행할 <em>언제</em>를 정의하고 전자는 실제 교차로의 상황을 나타냅니다(Observer의 비동기 특성으로 인해 <code>threshold</code>와 약간 다름).
벌써부터 낯익지 않나요? 네, intersectionRatio 속성은 Observer 설정의 threshold 속성과 유사합니다. 차이점은 후자는 Observer를 실행할 *시기*를 정의하고 전자는 실제 교차로의 상황을 나타냅니다(Observer의 비동기 특성으로 인해 threshold 과 약간 다름).

target 은 꽤 자주 접근해야 하는 IntersectionObserverEntry 인터페이스의 또 다른 속성입니다. 그러나 여기에는 절대적인 마법이 없습니다. Observer의 observe() 함수에 전달된 원래 요소일 뿐입니다. 이벤트로 작업할 때 익숙한 event.target 과 같습니다.

IntersectionObserverEntry 인터페이스의 전체 속성 목록을 보려면 사양을 확인하십시오.

가능한 응용

나는 당신이 이 챕터 때문에 이 기사를 보았을 가능성이 높다는 것을 알고 있습니다. 결국 복사 및 붙여넣기를 위한 코드 조각이 있을 때 메커니즘에 대해 누가 신경을 쓰겠습니까? 이제 더 이상 논의하지 않아도 됩니다. 이제 코드와 예제의 영역으로 들어가고 있습니다. 코드에 포함된 주석이 상황을 더 명확하게 해주기를 바랍니다.

지연된 기능

우선, IntersectionObserver 아이디어의 기초가 되는 기본 원칙을 보여주는 예를 검토해 보겠습니다. 화면에 표시되면 많은 계산을 수행해야 하는 요소가 있다고 가정해 보겠습니다. 예를 들어 광고는 실제로 사용자에게 표시된 경우에만 조회를 등록해야 합니다. 하지만 이제 페이지의 첫 번째 화면 아래 어딘가에 자동 재생되는 캐러셀 요소가 있다고 가정해 보겠습니다.

애플리케이션의 첫 번째 화면 아래에 있는 캐러셀
캐러셀이나 애플리케이션의 접는 부분 아래에 무거운 기능이 있는 경우 즉시 부트스트랩/로드를 시작하는 것은 리소스 낭비입니다.

일반적으로 캐러셀을 실행하는 것은 무거운 작업입니다. 일반적으로 JavaScript 타이머, 요소를 자동으로 스크롤하는 계산 등이 포함됩니다. 이러한 모든 작업은 메인 스레드를 로드하고 자동 재생 모드에서 완료되면 메인 스레드가 이 적중을 받는 시점을 알기 어렵습니다. 우리가 첫 화면에서 콘텐츠의 우선 순위를 정하는 것에 대해 이야기하고 가능한 한 빨리 첫 번째 의미 있는 페인트 및 Time To Interactive를 실행하고 싶을 때 차단된 메인 스레드는 성능에 병목 현상이 됩니다.

이 문제를 해결하기 위해 이러한 캐러셀이 브라우저의 뷰포트에 들어갈 때까지 재생을 연기할 수 있습니다. 이 경우 IntersectionObserverEntry 인터페이스의 isIntersecting 매개변수에 대한 지식과 예제를 사용합니다.

 const carousel = document.getElementById('carousel'); let isLeaving = false; let observer = new IntersectionObserver(function(entries) { entries.forEach(entry => { if (entry.isIntersecting) { isLeaving = true; entry.target.startCarousel(); } else if (isLeaving) { isLeaving = false; entry.target.stopCarousel(); } }); } observer.observe(carousel);

여기서는 뷰포트에 들어갈 때만 회전 목마를 재생합니다. IntersectionObserver 의 초기화에 전달된 config 객체가 없음을 주목하십시오. 이것은 우리가 기본 구성 옵션에 의존한다는 것을 의미합니다. 회전 목마가 뷰포트에서 나오면 더 이상 중요하지 않은 요소에 리소스를 사용하지 않도록 재생을 중지해야 합니다.

자산의 지연 로딩

이것은 아마도 IntersectionObserver 의 가장 명백한 사용 사례일 것입니다. 우리는 사용자에게 지금 필요하지 않은 것을 다운로드하는 데 리소스를 사용하고 싶지 않습니다. 이것은 사용자에게 엄청난 이점을 제공합니다. 사용자는 다운로드할 필요가 없고 모바일 장치는 현재 필요하지 않은 많은 쓸모없는 정보를 구문 분석하고 컴파일할 필요가 없습니다. 당연히 앱 성능에도 도움이 됩니다.

스크롤 없이 볼 수 있는 이미지 로드 지연
첫 번째 화면 아래에 있는 이미지와 같은 지연 로딩 자산 – IntersectionObserver의 가장 확실한 애플리케이션.

이전에는 사용자가 화면에 리소스를 표시할 수 있을 때까지 리소스 다운로드 및 처리를 연기하기 위해 scroll 과 같은 이벤트에서 이벤트 리스너를 처리했습니다. 문제는 분명합니다. 이것은 청취자를 너무 자주 트리거했습니다. 그래서 우리는 콜백의 실행을 조절하거나 디바운싱하는 아이디어를 생각해 내야 했습니다. 그러나 이 모든 것은 우리가 가장 필요할 때 이를 차단하는 메인 스레드에 많은 압력을 가했습니다.

따라서 지연 로딩 시나리오에서 IntersectionObserver 로 돌아가서 무엇을 주시해야 할까요? 지연 로딩 이미지의 간단한 예를 살펴보겠습니다.

CodePen에서 Denys Mishunov(@mishunov)의 IntersectionObserver에서 Pen Lazy 로딩을 참조하십시오.

CodePen에서 Denys Mishunov(@mishunov)의 IntersectionObserver에서 Pen Lazy 로딩을 참조하십시오.

해당 페이지를 "세 번째 화면"으로 천천히 스크롤하고 오른쪽 상단 모서리에 있는 모니터링 창을 보십시오. 지금까지 얼마나 많은 이미지가 다운로드되었는지 알려줍니다.

이 작업을 위한 HTML 마크업의 핵심에는 간단한 이미지 시퀀스가 ​​있습니다.

 … <img data-src="https://blah-blah.com/foo.jpg"> …

보시다시피, 이미지는 src 태그 없이 와야 합니다. 브라우저가 src 속성을 보면 우리의 의도와 반대되는 이미지를 즉시 다운로드하기 시작합니다. 따라서 HTML의 이미지에 해당 속성을 추가해서는 안 되며 대신 여기에서 data-src 와 같은 일부 data- 속성에 의존할 수 있습니다.

이 솔루션의 또 다른 부분은 물론 JavaScript입니다. 여기에서 주요 내용에 집중해 보겠습니다.

 const images = document.querySelectorAll('[data-src]'); const config = { … }; let observer = new IntersectionObserver(function (entries, self) { entries.forEach(entry => { if (entry.isIntersecting) { … } }); }, config); images.forEach(image => { observer.observe(image); });

구조적 측면에서 새로운 것은 없습니다. 이전에 이 모든 것을 다루었습니다.

  • data-src 속성으로 모든 메시지를 받습니다.
  • 설정 config : 이 시나리오의 경우 "캡처 프레임"을 확장하여 뷰포트 하단보다 약간 낮은 요소를 감지하려고 합니다.
  • 해당 구성으로 IntersectionObserver 를 등록하십시오.
  • 이미지를 반복하고 이 IntersectionObserver 에서 관찰할 모든 이미지를 추가합니다.

흥미로운 부분은 항목에 대해 호출된 콜백 함수 내에서 발생합니다. 여기에는 세 가지 필수 단계가 포함됩니다.

  1. 우선 '캡처 프레임'과 실제로 교차하는 항목만 처리합니다. 지금쯤이면 이 스니펫이 익숙할 것입니다.

     entries.forEach(entry => { if (entry.isIntersecting) { … } });

  2. 그런 다음 data-src 가 있는 이미지를 실제 <img src="…"> 로 변환하여 항목을 처리합니다.

     if (entry.isIntersecting) { preloadImage(entry.target); … }
    그러면 브라우저가 마침내 이미지를 다운로드하게 됩니다. preloadImage() 는 여기서 언급할 가치가 없는 매우 간단한 함수입니다. 소스만 읽어보세요.

  3. 다음이자 마지막 단계: 지연 로딩은 일회성 작업이고 요소가 "캡처 프레임"에 들어갈 때마다 이미지를 다운로드할 필요가 없기 때문에 이미 처리된 이미지를 unobserve 하지 않아야 합니다. 코드에서 메모리 누수를 방지하기 위해 더 이상 필요하지 않은 일반 이벤트에 대해 element.removeEventListener() 를 사용하여 수행해야 하는 것과 같은 방식입니다.

     if (entry.isIntersecting) { preloadImage(entry.target); // Observer has been passed as self to our callback self.unobserve(entry.target); }

메모. unobserve(event.target) 대신 disconnect() 를 호출할 수도 있습니다. IntersectionObserver 의 연결을 완전히 끊고 더 이상 이미지를 관찰하지 않습니다. 이것은 당신이 관심 있는 유일한 것이 관찰자에 대한 첫 번째 적중인 경우에 유용합니다. 우리의 경우 이미지를 계속 모니터링하려면 Observer가 필요하므로 아직 연결을 끊지 않아야 합니다.

자유롭게 예제를 포크하고 다양한 설정과 옵션을 사용해 보십시오. 특히 이미지를 지연 로드하려는 경우 언급할 흥미로운 것이 있습니다. 관찰된 요소에 의해 생성된 상자를 항상 염두에 두어야 합니다! 예제를 확인하면 41-47행의 이미지용 CSS에 중복된 스타일이 포함되어 있음을 알 수 있습니다. min-height: 100px . 이것은 이미지 자리 표시자( src 속성이 없는 <img> )에 약간의 수직 치수를 제공하기 위해 수행됩니다. 무엇 때문에?

  • 수직 치수가 없으면 모든 <img> 태그는 0×0 상자를 생성합니다.
  • <img> 태그는 기본적으로 일종의 inline-block 상자를 생성하기 때문에 모든 0x0 상자는 같은 줄에 나란히 정렬됩니다.
  • 이것은 IntersectionObserver 가 모든(또는 스크롤 속도에 따라 거의 모든) 이미지를 한 번에 등록한다는 것을 의미합니다. 아마도 원하는 것이 아닐 것입니다.

현재 섹션의 강조 표시

IntersectionObserver 는 물론 지연 로딩 그 이상입니다. 다음은 scroll 이벤트를 이 기술로 대체하는 또 다른 예입니다. 이 시나리오에는 매우 일반적인 시나리오가 있습니다. 고정 탐색 모음에서 문서의 스크롤 위치를 기반으로 현재 섹션을 강조 표시해야 합니다.

CodePen의 Denys Mishunov(@mishunov)의 IntersectionObserver에서 Pen Highlighting 현재 섹션을 참조하십시오.

CodePen의 Denys Mishunov(@mishunov)의 IntersectionObserver에서 Pen Highlighting 현재 섹션을 참조하십시오.

구조적으로 이미지를 지연 로드하는 예와 유사하며 다음을 제외하고 기본 구조가 동일합니다.

  • 이제 우리는 이미지가 아니라 페이지의 섹션을 관찰하고 싶습니다.
  • 분명히 우리는 콜백의 항목을 처리하는 다른 함수도 가지고 있습니다( intersectionHandler(entry) ). 그러나 이것은 흥미롭지 않습니다. CSS 클래스를 토글하는 것뿐입니다.

여기서 흥미로운 점은 config 개체입니다.

 const config = { rootMargin: '-50px 0px -55% 0px' };

rootMargin 의 기본값 0px 가 아닌 이유는 무엇입니까? 글쎄, 단순히 현재 섹션을 강조 표시하는 것과 이미지를 지연 로드하는 것은 우리가 달성하려는 것과 상당히 다르기 때문입니다. 지연 로딩을 사용하면 이미지가 보기에 들어오기 전에 로딩을 시작하려고 합니다. 따라서 이를 위해 하단에서 "캡처 프레임"을 50px 확장했습니다. 반대로 현재 섹션을 강조 표시하려면 해당 섹션이 실제로 화면에 표시되는지 확인해야 합니다. 뿐만 아니라 사용자가 실제로 이 섹션을 읽고 있거나 읽을 예정인지 확인해야 합니다. 따라서 활성 섹션을 선언하기 전에 섹션이 아래쪽에서 뷰포트의 절반 이상으로 이동하기를 원합니다. 또한 탐색 막대의 높이를 고려하여 "캡처 프레임"에서 막대의 높이를 제거합니다.

현재 섹션의 프레임 캡처
우리는 관찰자가 상단에서 50px에서 하단에서 뷰포트의 55% 사이의 '캡처 프레임'에 들어가는 요소만 감지하기를 원합니다.

또한 현재 탐색 항목을 강조 표시 하는 경우 관찰을 중단하고 싶지 않습니다. 여기에서는 항상 IntersectionObserver 를 유지해야 하므로 여기에서는 disconnect()unobserve() 를 찾을 수 없습니다.

요약

IntersectionObserver 는 매우 직관적인 기술입니다. 그것은 최신 브라우저에서 꽤 좋은 지원을 하고 있으며 여전히 지원하는(또는 전혀 지원하지 않는) 브라우저를 위해 구현하려는 경우 물론 이를 위한 폴리필이 있습니다. 그러나 대체로 이것은 뷰포트에서 요소 감지와 관련된 모든 종류의 작업을 수행하는 동시에 정말 좋은 성능 향상을 달성하는 데 도움이 되는 훌륭한 기술입니다.

IntersectionObserver가 당신에게 좋은 이유는 무엇입니까?

  • IntersectionObserver 는 비동기 비차단 API입니다!
  • IntersectionObserverscroll 또는 resize 이벤트에서 값비싼 리스너를 대체합니다.
  • IntersectionObservergetClientBoundingRect() 와 같은 모든 값비싼 계산을 수행하므로 필요하지 않습니다.
  • IntersectionObserver 는 다른 Observer의 구조적 패턴을 따르므로 이론적으로 다른 Observer가 작동하는 방식에 익숙하다면 이해하기 쉬울 것입니다.

염두에 두어야 할 사항

IntersectionObserver의 기능을 모든 것이 window.addEventListener('scroll') 세계와 비교하면 이 Observer에서 단점을 찾기 어려울 것입니다. 따라서 대신 염두에 두어야 할 몇 가지 사항을 알아보겠습니다.

  • 예, IntersectionObserver 는 비동기 비차단 API입니다. 이것은 아는 것이 좋습니다! 그러나 API 자체가 비동기인 경우에도 콜백에서 실행 중인 코드가 기본적으로 비동기적으로 실행되지 않는다는 것을 이해하는 것이 훨씬 더 중요합니다. 따라서 콜백 함수의 계산으로 인해 메인 스레드가 응답하지 않는 경우 IntersectionObserver 에서 얻을 수 있는 모든 이점을 제거할 수 있는 기회가 여전히 있습니다. 그러나 이것은 다른 이야기입니다.
  • 자산(예: 이미지)을 지연 로드하기 위해 IntersectionObserver 를 사용하는 경우 자산이 로드된 후 .unobserve(asset) 를 실행합니다.
  • IntersectionObserver 는 문서의 서식 구조에 나타나는 요소에 대해서만 교차를 감지할 수 있습니다. 명확히 하기 위해: 관찰 가능한 요소는 상자를 생성하고 레이아웃에 영향을 주어야 합니다. 다음은 더 나은 이해를 돕기 위한 몇 가지 예입니다.

    • display: none 요소는 없습니다.
    • opacity: 0 또는 visibility:hidden 상자를 생성하여(보이지 않더라도) 감지할 수 있습니다.
    • width:0px; height:0px 절대 위치 요소 width:0px; height:0px 괜찮습니다. Though, it has to be noted that absolutely positioned elements fully positioned outside of parent's borders (with negative margins or negative top , left , etc.) and are cut out by parent's overflow: hidden won't be detected: their box is out of scope for the formatting structure.
IntersectionObserver: Now You See Me
IntersectionObserver: Now You See Me

I know it was a long article, but if you're still around, here are some links for you to get an even better understanding and different perspectives on the Intersection Observer API:

  • Intersection Observer API on MDN;
  • IntersectionObserver polyfill;
  • IntersectionObserver polyfill as npm module;
  • Lazy-Loading Images with IntersectionObserver [video] by amazing Paul Lewis;
  • Basic and short (just 01:39), but very informative introduction to IntersectionObserver [video] by Surma.

With this, I would like to make a pause in our discussion to give you an opportunity to play with this technology and realize all of its convenience. So, go play with it. The article is finally over. This time I really mean it.