교차 관찰자

W3C 작업 초안,

이 문서에 대한 추가 정보
이 버전:
https://www.w3.org/TR/2023/WD-intersection-observer-20231018/
최신 공개 버전:
https://www.w3.org/TR/intersection-observer/
편집자 초안:
https://w3c.github.io/IntersectionObserver/
이전 버전:
역사:
https://www.w3.org/standards/history/intersection-observer/
테스트 스위트:
http://w3c-test.org/intersection-observer/
피드백:
GitHub
편집자:
(Google)
(Mozilla)
(Google)
이전 편집자:
(Google)

요약

이 명세서는 DOM 요소("대상")가 포함 요소 또는 최상위 뷰포트("루트")에 대해 얼마나 보이는지와 위치를 이해하는 데 사용할 수 있는 API를 설명합니다. 위치 정보는 비동기적으로 제공되며, 요소의 가시성을 파악하거나 DOM 콘텐츠의 사전 로딩 및 지연 로딩을 구현하는 데 유용합니다.

이 문서의 상태

이 섹션은 이 문서가 발행된 시점의 상태를 설명합니다. 현재 W3C 발행물 목록과 이 기술 보고서의 최신 개정본은 W3C 기술 보고서 색인에서 확인할 수 있습니다: https://www.w3.org/TR/.

이 문서는 웹 애플리케이션 작업 그룹에서 작업 초안으로 발행되었습니다. 이 문서는 W3C 권고안(Recommendation)이 되는 것을 목표로 합니다.

이 문서는 웹 애플리케이션 작업 그룹에서 권고안 트랙을 사용하여 작업 초안으로 발행되었습니다. 이 명세에 대한 피드백과 의견을 환영합니다. GitHub 이슈를 이용해 주세요. 과거 토론은 public-webapps@w3.org 아카이브에서 확인할 수 있습니다.

작업 초안으로 출판되었다고 해서 W3C 및 회원들의 지지를 의미하지 않습니다. 이 문서는 초안이며 언제든지 다른 문서로 업데이트, 대체 또는 폐지될 수 있습니다. 작업 중인 문서로서 인용하는 것은 적절하지 않습니다.

이 문서는 W3C 특허 정책에 따라 운영되는 그룹에서 작성되었습니다. W3C는 그룹의 산출물과 관련하여 제출된 공개 특허 공개 목록을 유지합니다. 해당 페이지에는 특허 공개 방법도 포함되어 있습니다. 특정 특허에 대해 필수 청구가 있다고 생각되는 경우, W3C 특허 정책 6절에 따라 정보를 공개해야 합니다.

이 문서는 2023년 6월 12일 W3C 프로세스 문서에 의해 관리됩니다.

1. 소개

웹의 기존 위치 계산 메커니즘은 DOM 상태에 대한 명시적 쿼리에 의존합니다. 이는 (비싼) 스타일 재계산 및 레이아웃을 유발하는 것으로 알려져 있으며, 자주 이 정보를 지속적으로 폴링하기 때문에 상당한 성능 오버헤드의 원인이 됩니다.

이러한 동작에 의존하는 일반적인 사례들이 발전해 왔으며, 예를 들면 다음과 같습니다:

이러한 사용 사례들은 몇 가지 공통적인 특성을 가집니다:

  1. 개별 요소의 상태에 대한 수동적 "쿼리"로 표현될 수 있습니다. 이는 다른 요소(또는 글로벌 뷰포트)에 대한 것입니다.

  2. 엄격한 지연 시간 요구사항이 없습니다. 즉, 정보가 비동기적으로(예: 다른 스레드에서) 전달되어도 문제가 없습니다.

  3. 대부분의 기존 웹 플랫폼 기능 조합에서 지원이 미흡하여, 광범위하게 사용됨에도 불구하고 개발자가 상당한 노력을 들여야 합니다.

주목할 만한 비목표는 실제로 표시된 픽셀 단위의 정확한 정보입니다. 이는 필터, webgl, 기타 기능이 있는 특정 브라우저 구조에서는 효율적으로 얻기 어렵기 때문입니다. 이러한 모든 상황에서 약간의 지연이 있더라도, 완벽한 합성 결과 데이터가 없어도 정보는 유용합니다.

Intersection Observer API는 위의 문제들을 해결하기 위해, 개발자가 요소의 위치를 다른 요소 또는 글로벌 뷰포트에 대해 비동기적으로 쿼리할 수 있는 새로운 방법을 제공합니다. 비동기 전달 방식으로 인해 비용이 많이 드는 DOM 및 스타일 쿼리, 지속적인 폴링, 커스텀 플러그인 사용이 필요 없어집니다. 이러한 방법들이 불필요해짐에 따라 애플리케이션은 CPU, GPU, 에너지 비용을 크게 줄일 수 있습니다.

var observer = new IntersectionObserver(changes => {
  for (const change of changes) {
    console.log(change.time);               // 변경이 발생한 타임스탬프
    console.log(change.rootBounds);         // 루트의 잘리지 않은 영역
    console.log(change.boundingClientRect); // target.getBoundingClientRect()
    console.log(change.intersectionRect);   // boundingClientRect, 포함 블록 조상에 의해 잘리고, rootBounds와 교차된 영역
    console.log(change.intersectionRatio);  // intersectionRect 영역과 boundingClientRect 영역의 비율
    console.log(change.target);             // 대상 요소(Element)
  }
}, {});

// 특정 대상 요소(Element)에 대한 교차 이벤트 감시.
observer.observe(target);

// 특정 대상 요소(Element)에 대한 교차 이벤트 감시 중지.
observer.unobserve(target);

// 모든 대상 요소에 대한 임계값 이벤트 감시 중지.
observer.disconnect();

2. 교차 관찰자

Intersection Observer API는 개발자가 대상 DOM 요소가 교차 루트에 대해 얼마나 보이는지와 위치를 이해할 수 있도록 해줍니다.

2.1. IntersectionObserverCallback

callback IntersectionObserverCallback = undefined (sequence<IntersectionObserverEntry> entries, IntersectionObserver observer);

이 콜백은 대상교차 루트의 교차에 변경이 있을 때 처리 모델에 따라 호출됩니다.

2.2. IntersectionObserver 인터페이스

IntersectionObserver 인터페이스는 교차 루트와 하나 이상의 대상 Element의 교차 변화를 관찰하는 데 사용할 수 있습니다.

교차 루트IntersectionObserverroot 속성 값이며, 이 속성이 null이 아닌 경우 그 값을 사용하고, 그렇지 않으면 최상위 브라우징 컨텍스트document 노드가 됩니다. 이 경우 암시적 루트라고 합니다.

IntersectionObserver에서 null이 아닌 root 를 가지면 명시적 루트 관찰자라고 하며, 대상 Element포함 블록 체인에서 root의 자손이면 모두 관찰할 수 있습니다. IntersectionObserver에서 null root 를 가지면 암시적 루트 관찰자라고 합니다. 암시적 루트 관찰자의 유효한 대상에는 Element최상위 브라우징 컨텍스트에 있거나, 중첩 브라우징 컨텍스트에 있는 Element도 포함됩니다.

암시적 루트 관찰자의 경우, API는 대상관련 설정 객체origin동일 오리진 도메인인지 여부에 따라 동일 오리진 도메인 대상교차 오리진 도메인 대상을 구분합니다. 명시적 루트 관찰자의 모든 대상동일 오리진 도메인 대상입니다. 대상교차 루트와 동일한 document에 있기 때문입니다.

참고: MutationObserver에서는 MutationObserverInit 옵션을 observe()에 전달하지만, IntersectionObserver에서는 생성자에 전달합니다. MutationObserver에서는 각 Node 마다 필터링할 속성 집합이 다를 수 있기 때문입니다. IntersectionObserver에서는 하나의 관찰자를 여러 대상에 동일한 옵션으로 사용하거나, 각 대상마다 다른 관찰자를 사용할 수 있습니다. rootMargin이나 threshold 값을 각 대상마다 다르게 지정하는 것은 복잡성만 높이고 추가적인 사용 사례를 해결하지 못하기 때문에, 필요하다면 추후에 observe() 별 옵션이 추가될 수 있습니다.

[Exposed=Window]
interface IntersectionObserver {
  constructor(IntersectionObserverCallback callback, optional IntersectionObserverInit options = {});
  readonly attribute (Element or Document)? root;
  readonly attribute DOMString rootMargin;
  readonly attribute DOMString scrollMargin;
  readonly attribute FrozenArray<double> thresholds;
  undefined observe(Element target);
  undefined unobserve(Element target);
  undefined disconnect();
  sequence<IntersectionObserverEntry> takeRecords();
};
new IntersectionObserver(callback, options)

callbackoptions를 제공하여 새 IntersectionObserver 초기화 알고리즘을 실행한 결과를 반환합니다.

observe(target)

thistarget을 제공하여 대상 요소 관찰 알고리즘을 실행합니다.

unobserve(target)

thistarget을 제공하여 대상 요소 관찰 중지 알고리즘을 실행합니다.

참고: MutationObserverunobserve()를 구현하지 않습니다. IntersectionObserver에서는 unobserve()가 지연 로딩(lazy-loading) 사용 사례를 해결합니다. target이 보이게 된 후에는 더 이상 추적할 필요가 없습니다. 모든 targetdisconnect()하고 남은 것만 observe()하거나, 각 target마다 별도의 IntersectionObserver를 만드는 것이 더 번거롭습니다.

disconnect()

this의 내부 [[ObservationTargets]] 슬롯에 있는 각 target에 대해:

  1. target의 내부 [[RegisteredIntersectionObservers]] 슬롯에서 IntersectionObserverRegistration 레코드 중 observer 속성이 this와 같은 것을 제거합니다.

  2. this의 내부 [[ObservationTargets]] 슬롯에서 target을 제거합니다.

takeRecords()
  1. this의 내부 [[QueuedEntries]] 슬롯의 복사본을 queue로 둡니다.

  2. this의 내부 [[QueuedEntries]] 슬롯을 비웁니다.

  3. queue를 반환합니다.

root, 타입 (Element or Document), readonly, nullable

rootIntersectionObserver 생성자에 전달된 값이며, 전달되지 않은 경우 null입니다.

rootMargin, 타입 DOMString, readonly

루트 교차 사각형에 적용되는 오프셋으로, 교차 계산에 사용되는 박스를 확장하거나 축소합니다. 이 오프셋은 동일 오리진 도메인 대상에만 적용되며, 교차 오리진 도메인 대상에는 무시됩니다.

값을 얻을 때, [[rootMargin]] 요소를 공백으로 구분해 직렬화하여 반환합니다. 픽셀 단위는 숫자 뒤에 "px"가 붙고, 퍼센트는 숫자 뒤에 "%"가 붙습니다. 이 값은 options.rootMargin 으로 생성자에 전달된 것과 동일함을 보장하지 않습니다. 아무 값도 전달되지 않은 경우 "0px 0px 0px 0px"입니다.

scrollMargin, 타입 DOMString, readonly

scrollport에서 교차 루트에서 대상까지의 경로에 있는 모든 스크롤 가능한 조상에 오프셋이 적용되어, 교차 계산에 사용되는 클립 영역을 확장 또는 축소합니다.

값을 얻을 때, [[scrollMargin]] 요소를 공백으로 구분해 직렬화하여 반환합니다. 픽셀 단위는 "px"가, 퍼센트는 "%"가 붙습니다. 이 값은 options.scrollMargin 으로 생성자에 전달된 것과 동일함을 보장하지 않습니다. 값이 없다면 "0px 0px 0px 0px"입니다.

thresholds, 타입 FrozenArray<double>, readonly

임계값의 리스트로, 오름차순으로 정렬된 숫자이며, 각각의 임계값은 관찰 대상의 교차 영역과 바운딩 박스 영역의 비율입니다. 대상의 임계값이 변경될 때 알림이 발생합니다. options.threshold 가 제공되지 않거나 시퀀스가 비어 있으면 이 속성 값은 [0]입니다.

Element 는, 계산된 스타일에 overflow 속성이 있어 요소의 패딩 엣지로 내용을 클립할 경우 콘텐츠 클립을 가진 것으로 정의합니다.

루트 교차 사각형IntersectionObserver 에서 대상과 비교할 때 사용하는 사각형입니다.

만약 IntersectionObserver암시적 루트 관찰자라면,
루트가 최상위 브라우징 컨텍스트document인 것처럼 처리합니다.
만약 교차 루트document라면,
document뷰포트 크기(이 단계는 document완전히 활성화된 경우에만 해당)
그 외에 교차 루트콘텐츠 클립을 가진다면,
요소의 패딩 영역이 됩니다.
그 외의 경우,
요소의 바운딩 박스 구하기 결과가 교차 루트가 됩니다.

루트 교차 사각형동일 오리진 도메인 대상에 대해 계산할 때, IntersectionObserver[[rootMargin]] 슬롯에 있는 오프셋에 따라 CSS의 margin 속성처럼 네 값이 각각 위, 오른쪽, 아래, 왼쪽 변이 얼마나 오프셋되는지 나타내며, 양수 길이는 바깥쪽으로 오프셋이 됨을 의미합니다. 퍼센트는 확장되지 않은 사각형의 너비를 기준으로 해석됩니다.

참고: rootMargin교차 루트에만 적용됩니다. 대상 Element 가 교차 루트가 아닌 조상에 의해 클립될 경우, 해당 클립에는 rootMargin이 영향을 주지 않습니다.

스크롤포트에 scroll margin을 적용하다

스크롤포트 교차 사각형을 동일 출처 도메인 대상에 대해 계산할 때, 사각형은 IntersectionObserver[[scrollMargin]] 슬롯에 있는 오프셋에 따라 확장됩니다. 이는 CSS의 margin 속성과 유사하게 동작하며, 네 개의 값은 각각 위, 오른쪽, 아래, 왼쪽 가장자리의 오프셋 크기를 나타냅니다. 양수 길이는 바깥쪽으로 오프셋됨을 의미합니다. 백분율은 확장되지 않은 사각형의 너비를 기준으로 해석됩니다.

이 오프셋은 동일 출처 도메인 대상을 처리할 때에만 적용되며, 교차 출처 도메인 대상에는 무시됩니다.

참고: scrollMargin대상이 모든 스크롤 가능한 조상(교차 루트 포함)에 의해 잘릴 때 영향을 줍니다. scrollMarginrootMargin 은 스크롤 가능한 교차 루트 사각형에도 적용됩니다.

참고: 루트 교차 사각형스크롤포트 교차 사각형은 핀치 줌의 영향을 받지 않으며 조정되지 않은 뷰포트를 보고합니다. 이는 핀치 줌의 의도(돋보기처럼 동작하며 레이아웃을 변경하지 않음)와 일치합니다.

margin을 파싱하다(root 또는 scroll) 입력 문자열 marginString에서 4개 픽셀 길이 또는 백분율의 리스트, 또는 실패를 반환합니다:

  1. 구성 값 리스트를 파싱 marginString에 대해 실행하고, 결과를 tokens로 저장합니다.

  2. tokens에서 모든 공백 토큰을 제거합니다.

  3. tokens의 길이가 4를 초과하면 실패를 반환합니다.

  4. tokens에 요소가 하나도 없으면 tokens를 ["0px"]로 설정합니다.

  5. tokens의 각 token을 다음과 같이 대체합니다:

    • token절대 길이 치수 토큰이면, 동일한 픽셀 길이로 대체합니다.

    • token<percentage> 토큰이면, 동일한 백분율로 대체합니다.

    • 그 외의 경우, 실패를 반환합니다.

  6. tokens에 요소가 하나만 있으면, 그 요소를 세 번 복제하여 tokens에 추가합니다. 두 요소가 있으면 각 요소를 한 번씩 복제하여 tokens에 추가합니다. 세 요소가 있으면 두 번째 요소를 한 번 더 복제하여 tokens에 추가합니다.

  7. tokens를 반환합니다.

2.3. IntersectionObserverEntry 인터페이스

[Exposed=Window]
interface IntersectionObserverEntry {
  constructor(IntersectionObserverEntryInit intersectionObserverEntryInit);
  readonly attribute DOMHighResTimeStamp time;
  readonly attribute DOMRectReadOnly? rootBounds;
  readonly attribute DOMRectReadOnly boundingClientRect;
  readonly attribute DOMRectReadOnly intersectionRect;
  readonly attribute boolean isIntersecting;
  readonly attribute double intersectionRatio;
  readonly attribute Element target;
};

dictionary IntersectionObserverEntryInit {
  required DOMHighResTimeStamp time;
  required DOMRectInit? rootBounds;
  required DOMRectInit boundingClientRect;
  required DOMRectInit intersectionRect;
  required boolean isIntersecting;
  required double intersectionRatio;
  required Element target;
};
boundingClientRect, 타입 DOMRectReadOnly, 읽기 전용

DOMRectReadOnlytarget에 대해 bounding box를 가져오기로 얻어집니다.

intersectionRect, 타입 DOMRectReadOnly, 읽기 전용

boundingClientRect에 대해 target의 모든 조상 clip rect(단, root는 제외)로 교차시킨 다음, root 교차 사각형과 교차시킨 값입니다. 이 값은 targetroot 교차 사각형 내에서 실제로 보이는 부분을 나타냅니다.

isIntersecting, 타입 boolean, 읽기 전용

targetroot와 교차하면 true, 아니면 false입니다. 이 플래그는 IntersectionObserverEntry가 교차 상태에서 비교차 상태로 전환됨을 신호하거나, IntersectionObserverEntry가 비교차 상태에서 교차 상태로 전환되지만 intersection rect의 영역이 0인 경우(가장자리에 맞닿거나 boundingClientRect의 영역이 0인 경우 등) 구별할 수 있게 해줍니다.

intersectionRatio, 타입 double, 읽기 전용

boundingClientRect에 영역이 0이 아니면, intersectionRect 영역을 boundingClientRect 영역으로 나눈 값입니다. 영역이 0이면 isIntersecting이 true면 1, 아니면 0입니다.

rootBounds, 타입 DOMRectReadOnly, 읽기 전용, null 허용

동일 출처 도메인 대상인 경우 root 교차 사각형입니다. 그 외에는 null입니다. 만약 target이 다른 browsing context에 있다면, intersection root와 다른 좌표계를 사용하게 됩니다. 이때 boundingClientRectintersectionRect도 다른 좌표계가 됩니다.

target, 타입 Element, 읽기 전용

intersection root와 교차가 변경된 Element입니다.

time, 타입 DOMHighResTimeStamp, 읽기 전용

이 속성은 IntersectionObserver 인스턴스가 알림을 생성할 때 기록된 교차 시점의 시간 원점 기준 DOMHighResTimeStamp를 반환해야 합니다.

2.4. IntersectionObserverInit 딕셔너리

dictionary IntersectionObserverInit {
  (Element or Document)?  root = null;
  DOMString rootMargin = "0px";
  DOMString scrollMargin = "0px";
  (double or sequence<double>) threshold = 0;
};
root, 타입 (Element or Document), null 허용, 기본값 null

교차에 사용할 root입니다. 제공하지 않으면 암시적 루트를 사용합니다.

rootMargin, 타입 DOMString, 기본값 "0px"

CSS의 margin 속성과 비슷하며, 1~4개의 구성 요소 문자열입니다. 각 값은 절대 길이 또는 백분율입니다.

"5px"                // 모든 margin이 5px
"5px 10px"           // 위 & 아래 = 5px, 오른쪽 & 왼쪽 = 10px
"-10px 5px 8px"      // 위 = -10px, 오른쪽 & 왼쪽 = 5px, 아래 = 8px
"-10px -5px 5px 8px" // 위 = -10px, 오른쪽 = -5px, 아래 = 5px, 왼쪽 = 8px
scrollMargin, 타입 DOMString, 기본값 "0px"

rootMargin과 비슷하며, 1~4개의 구성 요소 문자열입니다. 각 값은 절대 길이 또는 백분율입니다.

예시는 위의 rootMargin을 참고하세요.

threshold, 타입 (double or sequence<double>), 기본값 0

콜백을 트리거할 임계값(들)의 목록입니다. intersectionRect 영역이 임계값 이상에서 이하로(또는 그 반대) 변경될 때 콜백이 호출됩니다.

임계값 값은 [0, 1.0] 범위여야 하며, target에 대한 bounding box를 가져오기로 생성된 사각형 영역의 백분율을 나타냅니다.

참고: 0.0은 "픽셀이 1개라도 있으면"과 동일하게 동작합니다.

3. 처리 모델

이 섹션은 사용자 에이전트가 Intersection Observer API를 구현할 때 따라야 하는 단계들을 설명합니다.

3.1. 내부 슬롯 정의

3.1.1. 문서

documentIntersectionObserverTaskQueued 플래그를 가지며, 초기값은 false입니다.

3.1.2. 요소

Element 객체는 내부 [[RegisteredIntersectionObservers]] 슬롯을 가지며, 초기값은 빈 리스트입니다. 이 리스트는 IntersectionObserverRegistration 레코드를 포함합니다. 이 레코드는 observer 속성에 IntersectionObserver를 저장하고, previousThresholdIndex 속성에 옵저버의 thresholds 속성 길이(-1~포함)의 숫자를 저장하며, previousIsIntersecting 속성에 boolean 값을 저장합니다.

3.1.3. IntersectionObserver

IntersectionObserver 객체는 내부 [[QueuedEntries]][[ObservationTargets]] 슬롯을 가지며, 둘 다 빈 리스트로 초기화됩니다. 그리고 [[callback]] 슬롯이 있고, IntersectionObserver(callback, options)에서 초기화됩니다. 또한 내부 [[rootMargin]][[scrollMargin]] 슬롯이 있으며, 네 개의 픽셀 길이 또는 백분율의 리스트입니다.

3.2. 알고리즘

3.2.1. 새 IntersectionObserver 초기화

새 IntersectionObserver를 초기화하려면, IntersectionObserverCallback callbackIntersectionObserverInit 딕셔너리 options가 주어졌을 때, 다음 단계를 실행합니다:

  1. this를 새로운 IntersectionObserver 객체로 설정합니다.

  2. this의 내부 [[callback]] 슬롯을 callback으로 설정합니다.

  3. options.rootMargin에서 margin을 파싱하려고 시도합니다. 리스트가 반환되면, this의 내부 [[rootMargin]] 슬롯에 저장합니다. 그렇지 않으면, SyntaxError 예외를 throw합니다.

  4. options.scrollMargin에서 margin을 파싱하려고 시도합니다. 리스트가 반환되면, this의 내부 [[scrollMargin]] 슬롯에 저장합니다. 그렇지 않으면, SyntaxError 예외를 throw합니다.

  5. thresholdsoptions.threshold와 동일한 리스트로 설정합니다.

  6. thresholds에 0.0보다 작거나 1.0보다 큰 값이 있으면, RangeError 예외를 throw합니다.

  7. thresholds를 오름차순으로 정렬합니다.

  8. thresholds가 비어 있으면, 0thresholds에 추가합니다.

  9. thresholds 속성 getter는 이 정렬된 thresholds 리스트를 반환합니다.

  10. this를 반환합니다.

3.2.2. 대상 요소 관찰

대상 요소 관찰하려면, IntersectionObserver observerElement target이 주어졌을 때, 다음 단계를 따릅니다:

  1. targetobserver의 내부 [[ObservationTargets]] 슬롯에 있으면, return합니다.

  2. intersectionObserverRegistrationIntersectionObserverRegistration 레코드로 생성하고, observer 속성을 observer로, previousThresholdIndex 속성을 -1로, previousIsIntersecting 속성을 false로 설정합니다.

  3. intersectionObserverRegistrationtarget의 내부 [[RegisteredIntersectionObservers]] 슬롯에 추가합니다.

  4. targetobserver의 내부 [[ObservationTargets]] 슬롯에 추가합니다.

3.2.3. 대상 요소 관찰 중지

대상 요소 관찰 중지하려면, IntersectionObserver observerElement target이 주어졌을 때, 다음 단계를 따릅니다:

  1. IntersectionObserverRegistration 레코드 중 observer 속성이 this와 같은 것을 target의 내부 [[RegisteredIntersectionObservers]] 슬롯에서 제거합니다(있으면).

  2. targetthis의 내부 [[ObservationTargets]] 슬롯에서 제거합니다(있으면).

3.2.4. Intersection Observer 작업 큐에 추가

IntersectionObserver 작업 소스작업 소스이며, § 3.2.5 IntersectionObserver 알림 작업 스케줄에 사용됩니다.

Intersection Observer 작업을 큐에 추가하려면 document document에 대해, 다음 단계를 실행합니다:

  1. documentIntersectionObserverTaskQueued 플래그가 true면 return합니다.

  2. documentIntersectionObserverTaskQueued 플래그를 true로 설정합니다.

  3. 작업 큐 추가IntersectionObserver 작업 소스에 대해 document이벤트 루프에서 IntersectionObserver 알림을 실행하도록 큐에 추가합니다.

3.2.5. IntersectionObserver 알림

IntersectionObserver 알림document document에 대해 실행하려면, 다음 단계를 따릅니다:

  1. documentIntersectionObserverTaskQueued 플래그를 false로 설정합니다.

  2. notify listdocument의 DOM 트리에 IntersectionObserverroot가 포함된 모든 객체의 리스트로 설정합니다.

  3. notify list의 각 IntersectionObserver 객체 observer에 대해, 다음 단계를 실행합니다:

    1. observer의 내부 [[QueuedEntries]] 슬롯이 비어 있으면, 계속 진행합니다.

    2. queueobserver의 내부 [[QueuedEntries]] 슬롯의 복사본으로 설정합니다.

    3. observer의 내부 [[QueuedEntries]] 슬롯을 비웁니다.

    4. callbackobserver의 내부 [[callback]] 슬롯의 값으로 설정합니다.

    5. callbackqueue를 첫 번째 인자로, observer를 두 번째 인자로, observer콜백 this 값으로 호출합니다. 예외가 발생하면, 예외를 보고합니다.

3.2.6. IntersectionObserverEntry 큐에 추가

IntersectionObserverEntry를 큐에 추가하려면 IntersectionObserver observer, document document, DOMHighResTimeStamp time, DOMRectrootBounds, boundingClientRect, intersectionRect, isIntersecting 플래그, Element target이 주어졌을 때, 다음 단계를 실행합니다:

  1. IntersectionObserverEntry를 생성하고, time, rootBounds, boundingClientRect, intersectionRect, isIntersecting, target을 전달합니다.

  2. 이를 observer의 내부 [[QueuedEntries]] 슬롯에 추가합니다.

  3. Intersection Observer 작업을 큐에 추가document에 대해 실행합니다.

3.2.7. 대상 요소와 루트의 교차 영역 계산

target 요소와 루트의 교차 영역 계산을 하려면 target targetintersection root root가 주어졌을 때, 다음 단계를 실행합니다:

  1. intersectionRecttarget에 대해 bounding box를 가져오기의 결과로 설정합니다.

  2. containertarget포함 블록으로 설정합니다.

  3. containerroot가 아닐 때 반복합니다:

    1. container중첩 브라우징 컨텍스트document인 경우, intersectionRect를 그 document뷰포트로 클리핑하고, container브라우징 컨텍스트 컨테이너로 변경합니다.

    2. intersectionRectcontainer의 좌표 공간으로 매핑합니다.

    3. container스크롤 컨테이너인 경우, IntersectionObserver[[scrollMargin]]container의 클립 rect에 스크롤포트에 scroll margin을 적용처럼 적용합니다.

    4. containercontent clip 또는 css clip-path 속성이 있으면, container의 클립을 intersectionRect에 적용하여 업데이트합니다.

    5. container브라우징 컨텍스트의 루트 요소인 경우, container브라우징 컨텍스트document로, 그렇지 않으면 container포함 블록으로 변경합니다.

  4. intersectionRectroot의 좌표 공간으로 매핑합니다.

  5. intersectionRectroot 교차 사각형과 교차시켜 업데이트합니다.

  6. intersectionRecttarget이 포함된 document뷰포트 좌표 공간으로 매핑합니다.

  7. intersectionRect를 반환합니다.

3.2.8. 교차 관찰 업데이트 단계 실행

교차 관찰 업데이트 단계를 실행하다Document document에서 타임스탬프 time이 주어졌을 때 다음 단계를 수행합니다:

  1. observer listdocument의 DOM 트리에 IntersectionObserverroot가 포함된 모든 객체의 리스트로 설정합니다. 최상위 브라우징 컨텍스트의 경우, 암시적 루트 옵저버도 포함합니다.

  2. observer list의 각 observer에 대해:

    1. rootBoundsobserver루트 교차 사각형으로 설정합니다.

    2. observer의 내부 [[ObservationTargets]] 슬롯에 있는 각 target에 대해, observe() 호출 순서대로 처리합니다:

      1. 다음과 같이 설정합니다:

        • thresholdIndex를 0으로.

        • isIntersecting를 false로.

        • targetRectx, y, width, height가 0인 DOMRectReadOnly로.

        • intersectionRectx, y, width, height가 0인 DOMRectReadOnly로.

      2. 만약 교차점 루트암시적 루트가 아니고, targetdocument교차점 루트와 같지 않은 경우, 11단계로 건너뛴다.

      3. intersection rootElement이며, targetintersection root포함 블록 체인의 자손이 아니면 11단계로 건너뜁니다.

      4. targetRecttarget에 대해 bounding box를 가져오기의 결과로 설정합니다.

      5. intersectionRect교차 영역 계산 알고리즘으로 targetobserverintersection root에 대해 실행한 결과로 설정합니다.

      6. targetAreatargetRect의 영역으로 설정합니다.

      7. intersectionAreaintersectionRect의 영역으로 설정합니다.

      8. isIntersectingtargetRectrootBounds가 교차하거나 가장자리에 인접해 있으면 true로 설정합니다. 교차 영역이 0이어도(rootBounds 또는 targetRect의 영역이 0인 경우) true입니다.

      9. targetArea가 0이 아니면 intersectionRatiointersectionArea / targetArea로. 아니면 isIntersecting이 true면 1, false면 0으로 설정합니다.

      10. thresholdIndexobserver.thresholdsintersectionRatio보다 큰 첫 번째 항목의 인덱스로, intersectionRatio가 마지막 값 이상이면 observer.thresholds의 길이로 설정합니다.

      11. intersectionObserverRegistrationtarget의 내부 [[RegisteredIntersectionObservers]] 슬롯에서 observer 속성이 observer인 레코드로 설정합니다.

      12. previousThresholdIndexintersectionObserverRegistrationpreviousThresholdIndex 값으로 설정합니다.

      13. previousIsIntersectingintersectionObserverRegistrationpreviousIsIntersecting 값으로 설정합니다.

      14. thresholdIndexpreviousThresholdIndex와 다르거나, isIntersectingpreviousIsIntersecting과 다르면, IntersectionObserverEntry 큐에 추가를 실행합니다. observer, time, rootBounds, targetRect, intersectionRect, isIntersecting, target을 전달합니다.

      15. thresholdIndexintersectionObserverRegistrationpreviousThresholdIndex에 할당합니다.

      16. isIntersectingintersectionObserverRegistrationpreviousIsIntersecting에 할당합니다.

3.3. IntersectionObserver 생명주기

IntersectionObserver 객체는 다음 두 조건이 모두 충족될 때까지 생존합니다:

IntersectionObserver 객체는 unobserve() 메서드가 대상 인자로 호출되거나, disconnect() 메서드가 호출될 때까지 계속 관찰합니다.

3.4. 외부 명세 통합

3.4.1. HTML 처리 모델: 이벤트 루프

Intersection Observer 처리 단계는 HTML 이벤트 루프 처리 모델의 "렌더링 업데이트" 단계의 하위 단계로 존재합니다.

3.4.2. 초기 IntersectionObserver 대상 대기

document 객체는 다음 조건을 모두 만족하는 IntersectionObserver 가 하나 이상 있을 때 초기 IntersectionObserver 대상 대기가 있다고 합니다:
  1. observerrootdocument에 포함됨(최상위 브라우징 컨텍스트의 경우 암시적 루트 옵저버도 포함됨)
  2. observer[[ObservationTargets]] 슬롯에 IntersectionObserverEntry 가 아직 큐에 추가되지 않은 target을 하나 이상 가지고 있음

HTML 이벤트 루프 처리 모델의 "렌더링 업데이트" 단계에서, "불필요한 렌더링" 단계는 다음 조건이 추가되어 렌더링 업데이트를 건너뛸 수 있습니다:

4. 접근성 고려사항

이 섹션은 비규범적입니다.

IntersectionObserver 명세(본 문서)의 핵심에는 알려진 접근성 고려사항이 없습니다. 그러나 이 명세를 활용하거나 참조하는 관련 명세 및 제안들이 있으며, 이들에는 자체적인 접근성 고려사항이 있을 수 있습니다. 특히, HTML § 2.5.7 지연 로딩 속성CSS Containment 2 § 4 요소의 콘텐츠를 완전히 숨기기: content-visibility 속성 명세는 HTML § 6.9 페이지 내 찾기, HTML § 6.6.3 tabindex 속성, 공간 네비게이션 등에 영향을 줄 수 있습니다.

5. 프라이버시 및 보안

이 섹션은 비규범적입니다.

이 API와 관련된 주요 프라이버시 문제는 교차 출처 iframe(즉, 교차-출처-도메인 대상 케이스)에서 실행되는 코드에 정보를 제공할 수 있다는 점입니다. 특히:

IntersectionObserver 등장 이전에도 웹 개발자들은 IntersectionObserver로 얻을 수 있는 정보를 다른 API를 기발하고(때로는 기괴하게) 활용해 추출했습니다. 실질적으로, 이 API는 이미 다른 방식으로 얻을 수 있던 정보 이상을 공개하지 않습니다.

또 다른 고려 사항은 IntersectionObserverDOMHighResTimeStamp를 사용한다는 점입니다. 이 값에도 자체적인 프라이버시 및 보안 고려사항이 있지만, IntersectionObserver가 타이밍 관련 익스플로잇에 취약할 가능성은 낮습니다. 타임스탬프는 렌더링 업데이트마다 최대 한 번만 생성되며(§ 3.4.1 HTML 처리 모델: 이벤트 루프 참조), 이는 일반적인 타이밍 공격에는 너무 드뭅니다.

6. 국제화

이 섹션은 비규범적입니다.

국제화 관련 알려진 문제는 없습니다.

7. 감사의 글

이 명세를 개선하는 데 기술적 의견과 제안을 제공한 모든 기여자에게 특별히 감사드립니다.

적합성

문서 관례

적합성 요구사항은 설명적 단언과 RFC 2119 용어의 조합으로 표현됩니다. 본 문서의 규범적 부분에서 “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, “OPTIONAL” 등의 주요 단어는 RFC 2119에서 설명된 대로 해석해야 합니다. 다만, 가독성을 위해 이 명세에서는 해당 단어를 모두 대문자로 표기하지 않습니다.

명세의 모든 텍스트는 규범적이며, 명시적으로 비규범적임을 표시한 섹션, 예시, 노트만 예외입니다. [RFC2119]

이 명세의 예시는 “예를 들어(for example)”라는 단어로 시작하거나 class="example"로 규범적 텍스트와 구분되어 제시됩니다. 예시:

이것은 정보성 예시입니다.

정보성 노트는 “Note”라는 단어로 시작하며 class="note"로 규범적 텍스트와 구분되어 제시됩니다. 예시:

Note, 이것은 정보성 노트입니다.

적합 알고리즘

알고리즘의 일부로 명령형으로 표현된 요구사항(예: "앞쪽 공백 문자를 모두 제거한다" 또는 "false를 반환하고 이 단계를 중단한다")는 알고리즘 도입에 사용된 주요 단어("must", "should", "may" 등)의 의미로 해석해야 합니다.

알고리즘 또는 특정 단계로 표현된 적합성 요구사항은 최종 결과가 동일하기만 하면 어떤 방식으로든 구현할 수 있습니다. 특히 이 명세에서 정의된 알고리즘은 이해하기 쉽도록 작성된 것이며, 성능을 위한 것이 아님을 명심해야 합니다. 구현자는 성능 최적화를 권장합니다.

색인

이 명세에서 정의한 용어

참조로 정의된 용어

참고문헌

규범적 참고문헌

[CSS-BOX-4]
Elika Etemad. CSS Box Model Module Level 4. 2022년 11월 3일. WD. URL: https://www.w3.org/TR/css-box-4/
[CSS-CONTAIN-2]
Tab Atkins Jr.; Florian Rivoal; Vladimir Levin. CSS Containment Module Level 2. 2022년 9월 17일. WD. URL: https://www.w3.org/TR/css-contain-2/
[CSS-OVERFLOW-3]
Elika Etemad; Florian Rivoal. CSS Overflow Module Level 3. 2023년 3월 29일. WD. URL: https://www.w3.org/TR/css-overflow-3/
[CSS-SYNTAX-3]
Tab Atkins Jr.; Simon Sapin. CSS Syntax Module Level 3. 2021년 12월 24일. CR. URL: https://www.w3.org/TR/css-syntax-3/
[CSS-VALUES-3]
Tab Atkins Jr.; Elika Etemad. CSS Values and Units Module Level 3. 2022년 12월 1일. CR. URL: https://www.w3.org/TR/css-values-3/
[CSS-VALUES-4]
Tab Atkins Jr.; Elika Etemad. CSS Values and Units Module Level 4. 2023년 4월 6일. WD. URL: https://www.w3.org/TR/css-values-4/
[CSSOM-VIEW-1]
Simon Pieters. CSSOM View Module. 2016년 3월 17일. WD. URL: https://www.w3.org/TR/cssom-view-1/
[DOM]
Anne van Kesteren. DOM Standard. 현행 표준. URL: https://dom.spec.whatwg.org/
[GEOMETRY-1]
Simon Pieters; Chris Harrelson. Geometry Interfaces Module Level 1. 2018년 12월 4일. CR. URL: https://www.w3.org/TR/geometry-1/
[HTML]
Anne van Kesteren; et al. HTML 표준. 현행 표준. URL: https://html.spec.whatwg.org/multipage/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. 1997년 3월. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL 표준. 현행 표준. URL: https://webidl.spec.whatwg.org/

IDL 색인

callback IntersectionObserverCallback = undefined (sequence<IntersectionObserverEntry> entries, IntersectionObserver observer);

[Exposed=Window]
interface IntersectionObserver {
  constructor(IntersectionObserverCallback callback, optional IntersectionObserverInit options = {});
  readonly attribute (Element or Document)? root;
  readonly attribute DOMString rootMargin;
  readonly attribute DOMString scrollMargin;
  readonly attribute FrozenArray<double> thresholds;
  undefined observe(Element target);
  undefined unobserve(Element target);
  undefined disconnect();
  sequence<IntersectionObserverEntry> takeRecords();
};

[Exposed=Window]
interface IntersectionObserverEntry {
  constructor(IntersectionObserverEntryInit intersectionObserverEntryInit);
  readonly attribute DOMHighResTimeStamp time;
  readonly attribute DOMRectReadOnly? rootBounds;
  readonly attribute DOMRectReadOnly boundingClientRect;
  readonly attribute DOMRectReadOnly intersectionRect;
  readonly attribute boolean isIntersecting;
  readonly attribute double intersectionRatio;
  readonly attribute Element target;
};

dictionary IntersectionObserverEntryInit {
  required DOMHighResTimeStamp time;
  required DOMRectInit? rootBounds;
  required DOMRectInit boundingClientRect;
  required DOMRectInit intersectionRect;
  required boolean isIntersecting;
  required double intersectionRatio;
  required Element target;
};

dictionary IntersectionObserverInit {
  (Element or Document)?  root = null;
  DOMString rootMargin = "0px";
  DOMString scrollMargin = "0px";
  (double or sequence<double>) threshold = 0;
};

MDN

IntersectionObserver/IntersectionObserver

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android55+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserver/disconnect

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserver/observe

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserver/root

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserver/rootMargin

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserver/takeRecords

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserver/thresholds

In all current engines.

Firefox55+Safari12.1+Chrome52+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserver/unobserve

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserver

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserverEntry/boundingClientRect

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserverEntry/intersectionRatio

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserverEntry/intersectionRect

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserverEntry/isIntersecting

In all current engines.

Firefox55+Safari12.1+Chrome58+
Opera?Edge79+
Edge (Legacy)16+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserverEntry/rootBounds

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserverEntry/target

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserverEntry/time

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserverEntry

In all current engines.

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?