ResizeObserver: jest podobna do document.onresize dla elementów.

ResizeObserver informuje, gdy rozmiar elementu się zmienia.

Przed ResizeObserver musisz dołączyć listenera do zdarzenia resize dokumentu, aby otrzymywać powiadomienia o każdej zmianie wymiarów widoku. W obsługniku zdarzeń musisz wtedy ustalić, które elementy zostały dotknięte zmianą, i wywołać odpowiednią procedurę, aby odpowiednio zareagować. Jeśli po zmianie rozmiaru elementu potrzebne były nowe wymiary elementu, trzeba było wywołać funkcję getBoundingClientRect() lub getComputedStyle(), co może spowodować nadmierne przetwarzanie układu, jeśli nie zadbasz o zbiorowe wszystkich odczytów i wszystkich zapisów.

Nie objęło to nawet przypadków, gdy rozmiar elementów zmienia się bez zmiany rozmiaru okna głównego. Na przykład dodanie nowych elementów podrzędnych, ustawienie stylu elementu display na none lub podobne działania mogą zmienić rozmiar elementu, jego elementów siostrzanych lub jego elementów nadrzędnych.

Dlatego ResizeObserver jest przydatnym typem danych. Reaguje na zmiany rozmiaru dowolnego z obserwowanych elementów, niezależnie od tego, co spowodowało zmianę. Zapewnia też dostęp do nowego rozmiaru obserwowanych elementów.

Obsługa przeglądarek

  • Chrome: 64.
  • Edge: 79.
  • Firefox: 69.
  • Safari: 13.1.

Źródło

Interfejs API

Wszystkie interfejsy API z sufiksem Observer, o których wspominaliśmy powyżej, mają prostą konstrukcję. ResizeObserver nie jest wyjątkiem. Tworzysz obiekt ResizeObserver i przekazujesz do konstruktora funkcję wywołania zwrotnego. Do funkcji wywołania zwrotnego przekazywana jest tablica obiektów ResizeObserverEntry (po jednym wpisie na każdy obserwowany element), która zawiera nowe wymiary elementu.

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);

Niektóre szczegóły

Co chcesz zgłosić?

Zazwyczaj ResizeObserverEntry zgłasza pole treści elementu za pomocą właściwości o nazwie contentRect, która zwraca obiekt DOMRectReadOnly. Pole treści to pole, w którym można umieścić treści. Jest to pole obramowania pomniejszone o wypełnienie.

Diagram modelu pola CSS.

Pamiętaj, że chociaż ResizeObserver zgłasza wymiary contentRect i wypełnienie, to nadzoruje tylko contentRect. Nie mylij pola contentRect z ramką ograniczającą elementu. Pole ograniczające zwracane przez funkcję getBoundingClientRect() to pole zawierające cały element i jego potomków. Pliki SVG są wyjątkiem od reguły, w której ResizeObserver zgłasza wymiary ramki ograniczającej.

Od Chrome 84 ResizeObserverEntry ma 3 nowe właściwości, które przekazują bardziej szczegółowe informacje. Każda z tych właściwości zwraca obiekt ResizeObserverSize zawierający właściwości blockSizeinlineSize. Te informacje dotyczą obserwowanego elementu w momencie wywołania funkcji zwrotnej.

  • borderBoxSize
  • contentBoxSize
  • devicePixelContentBoxSize

Wszystkie te elementy zwracają tablice tylko do odczytu, ponieważ mamy nadzieję, że w przyszłości będą obsługiwać elementy z większą liczbą fragmentów, które występują w scenariuszach z wieloma kolumnami. Obecnie te tablice zawierają tylko 1 element.

Platforma tych właściwości jest ograniczona, ale Firefox już obsługuje pierwsze dwie opcje.

Kiedy zgłaszasz problem?

Specyfikacja określa, że ResizeObserver powinna przetwarzać wszystkie zdarzenia zmiany rozmiaru przed malowaniem i po ułożeniu. Dzięki temu funkcja wywołania zwrotnego ResizeObserver jest idealnym miejscem do wprowadzania zmian w układzie strony. Ponieważ przetwarzanie ResizeObserver odbywa się między elementem layout a elementem paint, spowoduje to unieważnienie tylko elementu layout, a nie elementu paint.

mam

Możesz się zastanawiać, co się stanie, jeśli w funkcji wywołania zmienisz rozmiar obserwowanego elementu na ResizeObserver. Odpowiedź: natychmiast wywołasz kolejne połączenie z numeru, na który chcesz się dodzwonić. Na szczęście ResizeObserver ma mechanizm zapobiegający nieskończonym pętlom wywołania i cyklicznym zależnościom. Zmiany będą przetwarzane w tej samej klatce tylko wtedy, gdy element, którego rozmiar został zmieniony, znajduje się głębiej w drzewie DOM niż najpłytszy element przetworzony w poprzednim wywołaniu zwrotnym. W przeciwnym razie zostaną odroczone do następnej klatki.

Aplikacja

ResizeObserver umożliwia m.in. implementowanie zapytań dotyczących multimediów dla poszczególnych elementów. Obserwując elementy, możesz precyzyjnie określić punkty przełamania projektu i zmienić style elementów. W tym przykładzie drugie pole zmienia promień obramowania zgodnie z szerokością.

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)'));

Innym interesującym przykładem jest okno czatu. Problem, który pojawia się w typowym układzie rozmowy od góry do dołu, to pozycjonowanie przewijania. Aby nie wprowadzać użytkownika w błąd, warto przypiąć okno u dołu okna rozmowy, gdzie pojawiają się najnowsze wiadomości. Dodatkowo każda zmiana układu (np. przejście z orientacji poziomej na pionową i odwrotnie) powinna dać ten sam wynik.

ResizeObserver umożliwia napisanie pojedynczego fragmentu kodu, który zadba o oba scenariusze. Zmiana rozmiaru okna jest zdarzeniem, które ResizeObserver może rejestrować zgodnie z definicją, ale wywołanie appendChild() powoduje również zmianę rozmiaru tego elementu (chyba że ustawione jest overflow: hidden), ponieważ musi on zrobić miejsce dla nowych elementów. Mając to na uwadze, do osiągnięcia pożądanego efektu wystarczy kilka linii kodu:

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);

Dobrze, prawda?

Tutaj mogę dodać więcej kodu, aby obsłużyć przypadek, gdy użytkownik ręcznie przewinął w górę i chce, aby przewijanie było ograniczone do tego komunikatu, gdy pojawi się nowa wiadomość.

Innym przypadkiem użycia jest dowolny element niestandardowy, który ma swój własny układ. Do wersji ResizeObserver nie było niezawodnego sposobu na otrzymywanie powiadomień o zmianach wymiarów, aby można było ponownie rozmieścić elementy podrzędne.

Wpływ na interakcję do kolejnego wyrenderowania (INP)

Od interakcji do kolejnego wyrenderowania (INP) to dane, które określają ogólną responsywność strony w odniesieniu do interakcji użytkownika. Jeśli INP strony mieści się w „dobrym” progu (czyli wynosi 200 milisekund lub mniej), można uznać, że strona prawidłowo reaguje na interakcje użytkownika.

Czas wykonywania wywołań zwrotnych zdarzeń w odpowiedzi na interakcję użytkownika może znacząco wpływać na całkowity czas opóźnienia interakcji, ale nie jest to jedyny aspekt, który należy wziąć pod uwagę. Wskaźnik INP uwzględnia też czas potrzebny na kolejne wyrenderowanie interakcji. Jest to czas potrzebny na zakończenie renderowania, które jest wymagane do zaktualizowania interfejsu użytkownika w odpowiedzi na interakcję.

W przypadku ResizeObserver jest to ważne, ponieważ wywołanie, które wykonuje instancja ResizerObserver, jest wykonywane tuż przed renderowaniem. Jest to celowe działanie, ponieważ należy wziąć pod uwagę pracę wykonywaną w wywołaniu zwrotnym, która prawdopodobnie będzie wymagać zmiany w interfejsie użytkownika.

Zadbaj o to, by w wywołaniu zwrotnym ResizeObserver wykonywać tylko tyle czynności związanych z renderowaniem, ponieważ zbyt długi czas renderowania może powodować opóźnienia w wykonywaniu ważnych zadań przez przeglądarkę. Jeśli np. jakakolwiek interakcja zawiera funkcję wywołania zwrotnego, która powoduje uruchomienie funkcji wywołania zwrotnego ResizeObserver, wykonaj te czynności, aby zapewnić jak najpłynniejsze działanie:

  • Upewnij się, że selektory CSS są jak najprostsze, aby uniknąć nadmiernego przeliczania stylów. Ponowne obliczenia stylów mają miejsce tuż przed układem, a złożone selektory CSS mogą opóźniać operacje układu.
  • Nie wykonuj w wywołaniu zwrotnym ResizeObserver żadnych czynności, które mogą spowodować wymuszone przeformatowanie.
  • Czas potrzebny na zaktualizowanie układu strony zwiększa się wraz z liczbą elementów DOM na stronie. Dzieje się tak niezależnie od tego, czy strony używają funkcji ResizeObserver, ale wraz ze wzrostem złożoności struktury strony praca wykonywana w obsługiwanym wywołaniu zwrotnym ResizeObserver może stać się znacząca.

Podsumowanie

ResizeObserver jest dostępna we wszystkich głównych przeglądarkach i stanowi skuteczny sposób monitorowania zmiany rozmiaru elementów. Pamiętaj, aby nie opóźniać zbytnio renderowania za pomocą tego zaawansowanego interfejsu API.