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

ResizeObserver informuje o zmianie rozmiaru elementu.

Przed ResizeObserver trzeba było dołączyć detektor do zdarzenia resize dokumentu, aby otrzymywać powiadomienia o wszelkich zmianach wymiarów widocznego obszaru. W module obsługi zdarzeń trzeba znaleźć elementy, na które miała wpływ ta zmiana, i wywołać konkretną rutynę, aby zareagowała odpowiednio. Jeśli po zmianie rozmiaru potrzebne były nowe wymiary elementu, trzeba było wywołać funkcję getBoundingClientRect() lub getComputedStyle(), co może spowodować rozproszenie układu, jeśli nie zadbasz o grupowanie wszystkich odczytów i wszystkich zapisów.

Nie zarejestrowano nawet sytuacji, w których rozmiar elementów zmienia się bez zmiany rozmiaru okna głównego. Na przykład dołączenie nowych elementów podrzędnych, ustawienie stylu display elementu jako none lub podobne działania mogą zmienić rozmiar elementu, jego elementów potomnych lub nadrzędnych.

Dlatego właściwość ResizeObserver jest przydatnym elementem podstawowym. Reaguje na zmiany wielkości obserwowanych elementów niezależnie od tego, co spowodowało zmianę. Daje też dostęp do nowego rozmiaru obserwowanych elementów.

Obsługa przeglądarek

  • 64
  • 79
  • 69
  • 13.1

Źródło

API

Wszystkie interfejsy API z wymienionym powyżej sufiksem Observer mają prosty projekt interfejsu API. ResizeObserver nie jest wyjątkiem. Tworzysz obiekt ResizeObserver i przekazujesz do konstruktora wywołanie zwrotne. Wywołanie zwrotne jest przekazywane do tablicy zawierającej obiekty ResizeObserverEntry (po 1 wpisie na 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 jest zgłaszane?

Ogólnie ResizeObserverEntry przekazuje pole treści elementu za pomocą właściwości contentRect, która zwraca obiekt DOMRectReadOnly. Pole treści to pole, w którym można umieścić treści. To pole obramowania bez dopełnienia.

Diagram modelu pola CSS.

Pamiętaj, że chociaż funkcja ResizeObserver raportuje zarówno wymiary elementu contentRect, jak i dopełnienia, tylko ogląda contentRect. Nie myl contentRect z ramką ograniczającą elementu. Zgodnie z raportem getBoundingClientRect() pole ograniczające to pole, które zawiera cały element i jego elementy podrzędne. Wyjątek od tej reguły stanowią pliki SVG, gdzie ResizeObserver podaje wymiary ramki ograniczającej.

Od Chrome 84 ResizeObserverEntry ma 3 nowe właściwości, które dostarczają bardziej szczegółowych informacji. Każda z tych właściwości zwraca obiekt ResizeObserverSize zawierający właściwość blockSize i inlineSize. Informacje te dotyczą obserwowanego elementu w momencie wywołania wywołania zwrotnego.

  • borderBoxSize
  • contentBoxSize
  • devicePixelContentBoxSize

Wszystkie te elementy zwracają tablice tylko do odczytu, ponieważ w przyszłości oczekujemy, że będą obsługiwać elementy, które mają wiele fragmentów, co występuje w przypadku scenariuszy wielokolumnowych. Na razie te tablice będą zawierać tylko 1 element.

Platforma obsługuje te właściwości w ograniczonym zakresie, ale Firefox obsługuje już pierwsze dwie.

Kiedy jest raportowany?

Specyfikacja określa, że ResizeObserver ma przetwarzać wszystkie zdarzenia zmiany rozmiaru przed wyrenderowaniem i po układem. To sprawia, że wywołanie zwrotne ResizeObserver to idealne miejsce do wprowadzania zmian w układzie strony. Przetwarzanie ResizeObserver odbywa się pomiędzy układem a renderowaniem, dlatego unieważnia się tylko układ, a nie renderowanie.

Gotcha

Możesz zadawać sobie pytanie: co się stanie, jeśli zmienię rozmiar obserwowanego elementu w wywołaniu zwrotnym na ResizeObserver? Odpowiedź brzmi: aktywujesz kolejne wywołanie zwrotne. Na szczęście ResizeObserver ma mechanizm, który zapobiega nieskończonym pętlom wywołań zwrotnych i cyklicznym zależnościom. Zmiany są przetwarzane w tej samej ramce tylko wtedy, gdy zmieniony element znajduje się niżej w drzewie DOM niż element najmniejszy przetworzony w poprzednim wywołaniu zwrotnym. W przeciwnym razie zostaną odłożone do następnej klatki.

Aplikacja

Jedną z funkcji ResizeObserver jest implementacja zapytań o media w poszczególnych elementach. Dzięki obserwowaniu elementów możesz imperatycznie definiować punkty przerwania w projekcie i zmieniać style elementów. W tym przykładzie drugie pole zmieni 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. W typowym układzie rozmów od góry do dołu problem polega na przewijaniu. Aby uniknąć zmylenia użytkownika, warto, aby okno przylegało do dolnej części rozmowy, tam gdzie pojawiają się najnowsze wiadomości. Poza tym każda zmiana układu (np. zmiana orientacji telefonu z poziomej na pionową lub odwrotnie) powinna przebiegać tak samo.

ResizeObserver umożliwia napisanie pojedynczego fragmentu kodu do obsługi obu scenariuszy. Zmiana rozmiaru okna jest zdarzeniem, które ResizeObserver może zarejestrować z definicji. Wywołanie metody appendChild() powoduje też zmianę rozmiaru tego elementu (o ile nie jest ustawiony overflow: hidden), ponieważ musi zrobić miejsce na nowe elementy. Mając to na uwadze, do osiągnięcia pożądanego efektu potrzeba niewiele linijek:

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

Całkiem nieźle, co?

Tutaj mogę dodać więcej kodu, który będzie odpowiadał sytuacji, w której użytkownik przewinął stronę ręcznie i chce, aby przewijał się dalej, przytrzymując wiadomość, gdy nadejdzie nowa wiadomość.

Inny przypadek użycia dotyczy dowolnego elementu niestandardowego, który ma własny układ. Do ResizeObserver nie było wiarygodnego sposobu na otrzymywanie powiadomień o zmianie wymiarów elementów podrzędnych, by można było ponownie rozplanować ich elementy podrzędne.

Wpływ na interakcję z kolejnym wyrenderowaniem (INP)

Interakcja z kolejnym wyrenderowaniem (INP) to dane służące do pomiaru ogólnej responsywności strony w odniesieniu do interakcji użytkowników. Jeśli wartość INP strony mieści się w progu „dobrego”, czyli nie przekracza 200 milisekund, oznacza to, że strona jest w pełni reagowana na interakcje użytkownika z nią.

Czas potrzebny na wykonanie wywołań zwrotnych zdarzeń w odpowiedzi na interakcję użytkownika może w znacznym stopniu wpływać na całkowity czas oczekiwania interakcji, ale nie jest to jedyny aspekt wartości INP, który należy wziąć pod uwagę. INP bierze też pod uwagę czas potrzebny do następnego wyrenderowania interakcji. Jest to czas potrzebny na aktualizację interfejsu użytkownika po interakcji z renderowaniem.

Jeśli chodzi o ResizeObserver, jest to ważne, ponieważ wywołanie zwrotne uruchomione przez instancję ResizerObserver ma miejsce tuż przed renderowaniem. Jest to projektowane z myślą o pracy wykonywanej w wywołaniu zwrotnym, ponieważ jej efekt najprawdopodobniej będzie wymagać zmiany interfejsu.

Zadbaj o jak najmniejszą ilość pracy związanej z renderowaniem, jaką jest wymagane w wywołaniu zwrotnym ResizeObserver, ponieważ zbyt częste renderowanie może być przyczyną opóźnień w wykonywaniu ważnych zadań przez przeglądarkę. Jeśli np. jakakolwiek interakcja zawiera wywołanie zwrotne, które powoduje wywołanie zwrotne ResizeObserver, upewnij się, że wykonujesz te czynności, aby zapewnić jak najpłynniejsze działanie:

  • Aby uniknąć nadmiernego przeliczania stylów, zadbaj o to, aby selektory arkusza CSS były jak najprostsze. Ponowne obliczanie stylów ma miejsce tuż przed układem, a złożone selektory arkusza CSS mogą opóźniać operacje układu.
  • Unikaj wykonywania w wywołaniu zwrotnym ResizeObserver zadań, które mogą wywołać wymuszone przeformatowania.
  • Czas potrzebny na aktualizację układu strony zasadniczo zwiększa się wraz z liczbą jej elementów DOM. Bez względu na to, czy strony używają funkcji ResizeObserver, czy nie, to praca w wywołaniu zwrotnym ResizeObserver może stać się istotnym elementem wraz ze wzrostem złożoności struktury strony.

Podsumowanie

ResizeObserver jest dostępny we wszystkich głównych przeglądarkach i zapewnia skuteczny sposób monitorowania zmian rozmiaru elementów na poziomie elementu. Uważaj tylko, aby nie opóźniać zbytnio renderowania za pomocą tego zaawansowanego interfejsu API.