ResizeObserver: 요소의 document.onresize와 유사

ResizeObserver를 사용하면 요소의 크기가 변경되는 시점을 알 수 있습니다.

ResizeObserver 이전에는 문서의 resize 이벤트에 리스너를 연결하여 표시 영역의 크기가 변경되면 알림을 받아야 했습니다. 그러면 이벤트 핸들러에서 변경사항의 영향을 받은 요소를 파악하고 특정 루틴을 호출하여 적절히 반응해야 합니다. 크기 조절 후 요소의 새 크기가 필요한 경우 getBoundingClientRect() 또는 getComputedStyle()를 호출해야 했는데, 모든 읽기와 모든 쓰기를 일괄 처리하지 않으면 레이아웃 스래싱이 발생할 수 있습니다.

기본 창의 크기를 조절하지 않고 요소의 크기가 변경되는 사례도 다루지 않았습니다. 예를 들어 새 하위 요소를 추가하거나 요소의 display 스타일을 none로 설정하거나 유사한 작업을 통해 요소, 동위 요소 또는 상위 요소의 크기를 변경할 수 있습니다.

이러한 이유로 ResizeObserver는 유용한 프리미티브입니다. 변경 원인과 관계없이 관찰된 요소의 크기 변화에 반응합니다. 관찰된 요소의 새로운 크기에도 액세스할 수 있습니다.

브라우저 지원

  • 64
  • 79
  • 69
  • 13.1

소스

API

위에서 언급한 Observer 접미사가 있는 모든 API는 간단한 API 디자인을 공유합니다. ResizeObserver도 예외는 아닙니다. ResizeObserver 객체를 만들고 콜백을 생성자에 전달합니다. 콜백에는 요소의 새 크기가 포함된 ResizeObserverEntry 객체의 배열(관찰된 요소당 하나의 항목)이 전달됩니다.

var ro = new ResizeObserver(entries => {
  for (let entry of entries) {
    const cr = entry.contentRect;

    console.log('Element:', entry.target);
    console.log(`Element size: ${cr.width}px x ${cr.height}px`);
    console.log(`Element padding: ${cr.top}px ; ${cr.left}px`);
  }
});

// Observe one or multiple elements
ro.observe(someElement);

일부 세부정보

어떤 내용이 보고되나요?

일반적으로 ResizeObserverEntryDOMRectReadOnly 객체를 반환하는 contentRect라는 속성을 통해 요소의 콘텐츠 상자를 보고합니다. 콘텐츠 상자는 콘텐츠를 넣을 수 있는 상자입니다. 테두리 상자에서 패딩을 뺀 값입니다.

CSS 상자 모델의 다이어그램

ResizeObservercontentRect의 측정기준과 패딩을 모두 보고하지만 contentRect감시한다는 점에 유의해야 합니다. contentRect를 요소의 경계 상자와 혼동해서는 안 됩니다. getBoundingClientRect()에 보고된 경계 상자는 전체 요소와 그 하위 요소가 포함된 상자입니다. SVG는 규칙의 예외로, ResizeObserver는 경계 상자의 크기를 보고합니다.

Chrome 84부터 ResizeObserverEntry에 더 자세한 정보를 제공하는 세 가지 새로운 속성이 있습니다. 이러한 각 속성은 blockSize 속성과 inlineSize 속성이 포함된 ResizeObserverSize 객체를 반환합니다. 이 정보는 콜백이 호출될 때 관찰된 요소에 관한 정보입니다.

  • borderBoxSize
  • contentBoxSize
  • devicePixelContentBoxSize

이러한 항목은 모두 읽기 전용 배열을 반환합니다. 향후 다중 열 시나리오에서 발생하는 여러 프래그먼트가 있는 요소를 지원할 수 있을 것으로 예상되기 때문입니다. 현재 이러한 배열에는 요소 하나만 포함됩니다.

이러한 속성의 플랫폼 지원은 제한적이지만 처음 두 속성은 Firefox에서 이미 지원됩니다.

언제 보고되나요?

사양은 ResizeObserver가 페인트 전과 레이아웃 후에 모든 크기 조절 이벤트를 처리해야 한다고 규정합니다. 이렇게 하면 ResizeObserver의 콜백이 페이지 레이아웃을 변경하기에 이상적인 위치입니다. ResizeObserver 처리는 레이아웃과 페인트 사이에서 발생하므로 그렇게 하면 페인트가 아닌 레이아웃만 무효화됩니다.

알았어

콜백 내에서 관찰된 요소의 크기를 ResizeObserver로 변경하면 어떻게 될까요? 답은 바로 콜백에 대한 다른 호출을 바로 트리거하는 것입니다. 다행히 ResizeObserver에는 무한 콜백 루프와 순환 종속 항목을 방지하는 메커니즘이 있습니다. 크기 조정된 요소가 이전 콜백에서 처리된 shallowest 요소보다 DOM 트리에서 더 깊은 경우에만 변경사항이 동일한 프레임에서 처리됩니다. 그렇지 않으면 다음 프레임으로 지연됩니다.

애플리케이션

ResizeObserver를 통해 할 수 있는 한 가지 작업은 요소별 미디어 쿼리를 구현하는 것입니다. 요소를 관찰하여 디자인 중단점을 명령적으로 정의하고 요소의 스타일을 변경할 수 있습니다. 다음 에서는 두 번째 상자의 너비에 따라 테두리 반경이 변경됩니다.

const ro = new ResizeObserver(entries => {
  for (let entry of entries) {
    entry.target.style.borderRadius =
        Math.max(0, 250 - entry.contentRect.width) + 'px';
  }
});
// Only observe the second box
ro.observe(document.querySelector('.box:nth-child(2)'));

또 다른 흥미로운 예로 채팅 창이 있습니다. 일반적인 위에서 아래로 대화 레이아웃에서 발생하는 문제는 스크롤 위치 지정입니다. 사용자의 혼란을 방지하기 위해 창이 대화 하단에 고정되어 최신 메시지가 표시되는 것이 좋습니다. 또한 모든 종류의 레이아웃 변경 (휴대전화를 가로 모드에서 세로 모드로 또는 그 반대로 전환)도 동일하게 달성되어야 합니다.

ResizeObserver를 사용하면 시나리오를 모두 처리하는 단일 코드를 작성할 수 있습니다. 창 크기 조절은 ResizeObserver가 정의에 따라 캡처할 수 있는 이벤트이지만 overflow: hidden가 설정되지 않은 한 appendChild()를 호출하면 새 요소를 위한 공간을 만들어야 하므로 요소의 크기가 조절됩니다. 이 점을 고려하면 다음과 같이 몇 줄의 줄로 원하는 효과를 얻을 수 있습니다.

const ro = new ResizeObserver(entries => {
  document.scrollingElement.scrollTop =
    document.scrollingElement.scrollHeight;
});

// Observe the scrollingElement for when the window gets resized
ro.observe(document.scrollingElement);

// Observe the timeline to process new messages
ro.observe(timeline);

정말 깔끔하지요?

여기에서 사용자가 수동으로 위로 스크롤한 다음 새 메시지가 수신될 때 해당 메시지를 고수하기 위해 스크롤하려는 경우를 처리하는 코드를 추가할 수 있습니다.

또 다른 사용 사례는 자체 레이아웃을 실행하는 모든 종류의 맞춤 요소입니다. ResizeObserver까지는 하위 요소가 다시 배치될 수 있도록 크기가 변경될 때 알림을 받는 안정적인 방법이 없었습니다.

다음 페인트와의 상호작용 (INP)에 미치는 영향

다음 페인트와의 상호작용 (INP)은 사용자 상호작용에 대한 페이지의 전반적인 응답성을 측정하는 측정항목입니다. 페이지의 INP가 '양호' 기준(200밀리초 이하)이면 페이지가 사용자와의 상호작용에 안정적으로 반응한다고 판단할 수 있습니다.

사용자 상호작용에 대한 응답으로 이벤트 콜백을 실행하는 데 걸리는 시간이 상호작용의 총 지연 시간에 크게 영향을 미칠 수 있지만, 이것이 INP에서 고려해야 할 유일한 측면은 아닙니다. 또한 INP는 상호작용의 다음 페인트가 발생하는 데 걸리는 시간을 고려합니다. 상호작용에 응답하여 사용자 인터페이스를 업데이트하는 데 필요한 렌더링 작업을 완료하는 데 걸리는 시간입니다.

ResizeObserver의 경우 ResizerObserver 인스턴스가 실행하는 콜백이 렌더링 작업 직전에 발생하므로 이는 중요합니다. 이는 콜백에서 발생하는 작업을 고려해야 하므로 의도적으로 설계된 것입니다. 작업의 결과로 사용자 인터페이스를 변경해야 할 가능성이 매우 높기 때문입니다.

과도한 렌더링 작업으로 인해 브라우저가 중요한 작업을 하는 데 지연이 발생할 수 있으므로 ResizeObserver 콜백에서 필요한 최소한의 렌더링 작업은 실행해야 합니다. 예를 들어 상호작용에 ResizeObserver 콜백을 실행하는 콜백이 있는 경우 최대한 원활한 환경을 위해 다음을 실행해야 합니다.

  • 과도한 스타일 재계산 작업을 방지하려면 CSS 선택자를 최대한 단순하게 지정하세요. 스타일 재계산은 레이아웃 직전에 이루어지며 복잡한 CSS 선택자는 레이아웃 작업을 지연시킬 수 있습니다.
  • ResizeObserver 콜백에서 강제 리플로우를 트리거할 수 있는 작업을 실행하지 마세요.
  • 페이지 레이아웃을 업데이트하는 데 필요한 시간은 일반적으로 페이지의 DOM 요소 수에 따라 증가합니다. 이는 페이지의 ResizeObserver 사용 여부와 관계없이 사실이지만 페이지의 구조적 복잡성이 증가함에 따라 ResizeObserver 콜백에서 실행되는 작업이 중요할 수 있습니다.

결론

ResizeObserver모든 주요 브라우저에서 사용할 수 있으며 요소 수준에서 요소 크기 조절을 모니터링하는 효율적인 방법을 제공합니다. 다만 이 강력한 API로 렌더링을 너무 많이 지연하지 않도록 주의하세요.