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.
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.
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 blockSize
i inlineSize
. 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 zwrotnymResizeObserver
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.