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.
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.
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 blockSize
i inlineSize
. 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 ResizeObserver
wykonywać 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 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.