본문 바로가기

[JavaScript] Resize Observer

by mugglim 2023. 1. 7.

프롤로그

브라우저 창의 크기 변경될 때 콜백 함수를 실행시키려면 어떻게 할까요? 혹시, 저와 비슷하게 window.addEventListener('resize', callback)을 떠올리셨나요? 그렇다면, 특정 태그의 크기가 변경될 때는 어떻게 콜백 함수를 실행시킬까요? 이번 글에서는 Resize Observer을 사용해 문제를 해결해보고자 합니다.

최근 업무에서 특정 엘리먼트의 resize 이벤트를 다룰 기회가 있었습니다. 브라우저 창의 크기가 아닌, 특정 엘리먼트의 크기가 변경되므로 window 객체에서 이벤트를 관리하고 싶지는 않았습니다. 해결 방법을 찾아보다가 Resize Observer API을 알게 되었습니다. 공부한 내용을 남겨 둘 겸 Resize Oberserver를 정리하려고 합니다.

Resize Observer

Resize ObserverElement 크기가 변경을 관리할 수 있는 API입니다. 여기서 크기는 Element의 너비와 높이를 의미합니다.

여기서 질문! Resize Observer API은 너비가 변경되는 이벤트만 관찰할 수 있을까요? 정답은 NO 입니다. W3에 따르면, Resize Observer가 관찰할 수 있는 경우와 없는 경우는 아래와 같습니다.

관찰 가능한 경우

  • Element가 DOM에 삽입/삭제될 때
  • Element의 display가 none으로 변경될 때
  • Element가 렌더링 된 후, 크기가 변하는 경우

관찰 불가한 경우

  • CSS transform에 의해 크기가 변한 경우

Resize Observer API

이제 Resize Observer 사용을 위한 방법을 알아봅시다. 간단하게 코드를 통해 확인해 봅시다. 아래 코드는 ResizeObserver 구현체를 간단히 정리해본 코드입니다. (실제 구현체와 다를 수 있습니다.)

function ResizeObserver(callback) {
  let listeners = [];

  const observe = (target, options?) => {
    // listeners에 target을 등록합니다.
    listeners.push({ target, options });
  };

  const unobserve = (target) => {
    // listeners에서 target을 제외합니다.
    listeners = listeners.filter(({ listener }) => listener.target !== target);
  };

  const disconnect = () => {
    // listeners를 초기화합니다.
    listeners = [];
  };
}

Resize Observe을 사용하기 위해서, 우선 Resize Observer 인스턴스를 생성해야합니다. 여기서 callback 인자는 이벤트가 발생했을 때 실행되는 콜백 함수입니다.

  const onResize = (entries, observer) => {
    // entries 관찰 되고 있는 listeners을 의미합니다.
    // observers는 ResizeObserver의 인스턴스를 의미합니다.
  };

  // ResizeObserver 인스턴스를 생성하고 콜백 함수를 주입합니다.
  const resizeObserver = new ResizeObserver(onResize);

이제 Resize Observe가 관찰할 Element를 등록해야합니다. observe() 의 첫 번째 인자인 target은 관찰할 Element를 의미합니다. 두 번째 인자 options는 선택사항이며, Element의 box-sizing option을 결정할 수 있습니다. ( "border-box" |"content-box", |"device-pixel-content-box" )

  // ResizeObserver의 인스턴스의 관찰 목록에 someElement을 구독시킵니다.
  resizeObserver.observe(someElement);

그러면 관찰중인 Element의 구독을 해제하려면 어떻게 할까요? 두가지 방법이 존재합니다. unobserve()는 Resize Observer 인스턴스가 관찰 중인 특정 Element를 해제시킵니다. 이와 달리, disconnct()는 관찰 중인 모든 Element를 해제시킵니다.

간단한 예시

이해를 돕기 위해 간단한 예제를 작성해봅시다. 요구사항은 아래와 같습니다.

  1. $root 엘리먼트는 자식으로 클래스명인 boxtextarea태그를 가지고 있다.
  2. 클래스명이 box인 textarea 엘리먼트의 크기가 변경될 때, console.log('onResize')을 실행시켜야 한다.
  3. ResizeObserver을 활용해본다.

예시 코드

const $root = document.getElementById("app");
const $box = document.createElement("textarea");
$box.className = "box";

$root.appendChild($box);

const handleBoxResize = (entries, observer) => {
  for (const entry of entries) {
    console.log("onResize!");
    console.log(entry.target);
  }
};

const resizeObserver = new ResizeObserver(handleBoxResize);
resizeObserver.observe($box);

예시 코드

글을 마무리 하며

간단하게 ResizeObserver의 개념과 사용법을 알아봤습니다. 혹시라도 리액트로 ResizeObserver을 사용하는 방법에 대해 관심이 있으시다면, 제가 개발 중인 react-use-resize을 한번 살펴보시길 바랍니다. (스타 눌러주시면 감사하겠습니다..)

레퍼런스

댓글