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 tą 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 obejmowało to nawet sytuacji, gdy elementy zmieniają rozmiar 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 wyrostem Observer, o którym wspomnieliś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ć?

Ogólnie element ResizeObserverEntry przekazuje informacje o polu 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.

Schemat modelu pudełka CSS.

Pamiętaj, że chociaż ResizeObserver zgłasza wymiary contentRect i wypełnienie, to obserwuje tylko contentRect. Nie myl contentRect z ramką ograniczającą elementu. Pole ograniczające zwracane przez funkcję getBoundingClientRect() to pole zawierające cały element i jego potomków. Wyjątkiem są pliki SVG, w których przypadku ResizeObserver zwraca wymiary ograniczonego obszaru.

Od wersji 84 Chrome ResizeObserverEntry ma 3 nowe właściwości, które zapewniają 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 wywołania zwrotnego.

  • 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.

Obsługa tych właściwości na poszczególnych platformach jest ograniczona, ale Firefox już obsługuje te pierwsze.

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 layoutem a paintem, spowoduje to unieważnienie tylko layoutu, a nie painta.

Rozumiem

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

Fajnie, 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)

Czas od interakcji do kolejnego wyrenderowania (INP) to dane, które mierzą ogólną responsywność strony na interakcje użytkowników. 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ę z użytkownikiem może znacząco wpływać na całkowity czas oczekiwania na odpowiedź, 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.

Pamiętaj, aby w obsługiwanym wywołaniu zwrotnym ResizeObserverwykonywać jak najmniej renderowania, ponieważ nadmierne renderowanie może spowodować 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:

  • Zadbaj o to, aby selektory CSS były jak najprostsze, aby uniknąć nadmiernego przeliczania stylów. Przeliczenia stylów mają miejsce tuż przed układem, a złożone selektory CSS mogą opóźniać operacje układu.
  • Unikaj wykonywania w funkcji ResizeObserver callback jakichkolwiek działań, które mogą powodować wymuszone ponowne przepływy danych.
  • 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.