ResizeObserver
מאפשרת לכם לדעת מתי הגודל של רכיב משתנה.
לפני ResizeObserver
, צריך היה לצרף מאזין לאירוע resize
של המסמך כדי לקבל התראה על כל שינוי במימדים של אזור התצוגה. לאחר מכן, בטיפול באירוע, תצטרכו לבדוק אילו רכיבים הושפעו מהשינוי ולקרוא לרוטינת ספציפית כדי להגיב בצורה מתאימה. אם הייתם צריכים לדעת מהם המאפיינים החדשים של רכיב אחרי שינוי הגודל שלו, הייתם צריכים להפעיל את getBoundingClientRect()
או את getComputedStyle()
. הפעולות האלה עלולות לגרום לטרחה בפריסת הרכיבים אם לא תבצעו קיבוץ של כל הקריאות וכל הכתיבות.
הבעיה הזו לא נגעה אפילו למקרים שבהם הגודל של רכיבים משתנה בלי שינוי הגודל של החלון הראשי. לדוגמה, הוספת צאצאים חדשים, הגדרת הסגנון display
של רכיב ל-none
או פעולות דומות יכולות לשנות את הגודל של רכיב, של אחיו או של אביו.
לכן, ResizeObserver
הוא רכיב שימושי. הוא מגיב לשינויים בגודל של כל אחד מהרכיבים שנצפו, ללא קשר למה שגרם לשינוי.
הוא גם מספק גישה לגודל החדש של האלמנטים שנצפו.
API
כל ממשקי ה-API עם הסיומת Observer
שציינו למעלה חולקים עיצוב API פשוט. ResizeObserver
הוא לא יוצא מן הכלל. יוצרים אובייקט ResizeObserver
ומעבירים קריאה חוזרת למבנה (constructor). ל-callback מועבר מערך של אובייקטים מסוג ResizeObserverEntry
– רשומה אחת לכל רכיב שנצפה – שמכיל את המאפיינים החדשים של הרכיב.
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);
כמה פרטים
מה נכלל בדיווח?
בדרך כלל, רכיב ResizeObserverEntry
מדווח על תיבת התוכן של רכיב באמצעות מאפיין שנקרא contentRect
, שמחזיר אובייקט DOMRectReadOnly
. תיבת התוכן היא התיבה שבה אפשר להציב תוכן. היא התיבה עם הגבול, פחות המילוי.
חשוב לציין ש-ResizeObserver
מדווח על המאפיינים של contentRect
ועל המילוי, אבל הוא צופה רק ב-contentRect
.
אין להתבלבל בין contentRect
לבין תיבת הגבול של האלמנט. התיבה המקיפה, כפי שמדווחת על ידי getBoundingClientRect()
, היא התיבה שמכילה את הרכיב כולו ואת הצאצאים שלו. קובצי SVG הם יוצאי דופן מהכלל, ופונקציית ResizeObserver
תדווח על המימדים של תיבת הגבול.
החל מגרסה 84 של Chrome, ל-ResizeObserverEntry
יש שלושה מאפיינים חדשים שמספקים מידע מפורט יותר. כל אחד מהנכסים האלה מחזיר אובייקט ResizeObserverSize
שמכיל את המאפיין blockSize
ואת המאפיין inlineSize
. המידע הזה מתייחס לרכיב שנצפה בזמן ההפעלה של פונקציית הקריאה החוזרת.
borderBoxSize
contentBoxSize
devicePixelContentBoxSize
כל הפריטים האלה מחזירים מערכי קריאה בלבד, כי אנחנו מקווים שבעתיד הם יוכלו לתמוך ברכיבים עם כמה קטעים, שמתרחשים בתרחישים עם כמה עמודות. בשלב זה, המערכים האלה יכללו רק רכיב אחד.
התמיכה בפלטפורמות בנכסים האלה מוגבלת, אבל Firefox כבר תומך בשניים הראשונים.
מתי מתבצע הדיווח?
לפי המפרט, ResizeObserver
צריך לעבד את כל אירועי שינוי הגודל לפני הצביעה ואחרי הפריסה. לכן, קריאה חוזרת של ResizeObserver
היא המקום האידיאלי לבצע שינויים בפריסת הדף. מאחר שעיבוד ResizeObserver
מתבצע בין הפריסה לבין הצביעה, הפעולה הזו תגרום לפסילה של הפריסה בלבד, ולא של הצביעה.
הבנתי
יכול להיות שתהיו סקרנים לדעת מה קורה אם משנים את הגודל של רכיב שנצפה בתוך פונקציית ה-callback ל-ResizeObserver
. התשובה היא: תגרמו להפעלה מיידית של שיחה חוזרת. למרבה המזל, ל-ResizeObserver
יש מנגנון שמאפשר למנוע לולאות קריאה חוזרת אינסופיות ותלות ציקלית. השינויים יטופלו באותו פריים רק אם האלמנט ששיניתם את הגודל שלו נמצא עמוק יותר בעץ ה-DOM מאשר האלמנט השטוח ביותר שעובד בקריאה החוזרת הקודמת.
אחרת, הם יועברו למסגרת הבאה.
אפליקציה
אחת מהאפשרויות שמאפשרת ResizeObserver
היא להטמיע שאילתות מדיה לכל רכיב. כשבודקים את הרכיבים, אפשר להגדיר באופן גורף את נקודות העצירה של העיצוב ולשנות את הסגנונות של הרכיבים. בדוגמה הבאה, הרדיוס של גבולות התיבה השנייה ישתנה בהתאם לרוחב שלה.
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)'));
דוגמה מעניינת נוספת היא חלון צ'אט. הבעיה שמתעוררת בפריסה רגילה של שיחה מלמעלה למטה היא מיקום הגלילה. כדי למנוע בלבול אצל המשתמש, מומלץ שהחלון יישאר בתחתית השיחה, שם מופיעות ההודעות החדשות ביותר. בנוסף, כל שינוי של הפריסה (למשל, שינוי של הטלפון מפורמט לרוחב לפורמט לאורך או להפך) אמור להשיג את אותו אפקט.
ResizeObserver
מאפשר לכתוב קטע קוד יחיד שיטפל בשני התרחישים. שינוי גודל החלון הוא אירוע ש-ResizeObserver
יכול לתעד לפי הגדרה, אבל קריאה ל-appendChild()
משנה גם את גודל הרכיב הזה (אלא אם מגדירים את overflow: hidden
), כי צריך לפנות מקום לרכיבים החדשים. בהתאם לכך, צריך רק כמה שורות כדי להשיג את האפקט הרצוי:
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);
מגניב, לא?
מכאן אפשר להוסיף קוד נוסף כדי לטפל במקרה שבו המשתמש גולל למעלה באופן ידני, והוא רוצה שהגלילה תישאר בהודעה הזו כשהודעה חדשה תגיע.
תרחיש לדוגמה נוסף הוא כל סוג של רכיב מותאם אישית שיש לו פריסה משלו.
עד ResizeObserver
, לא הייתה דרך מהימנה לקבל התראה כשהמימדים שלו משתנים, כדי שאפשר יהיה למקם מחדש את הצאצאים שלו.
השפעות על מהירות התגובה לאינטראקציה באתר (INP)
מהירות התגובה לאינטראקציה באתר (INP) הוא מדד שמודד את רמת הרספונסיביות הכוללת של הדף לאינטראקציות של משתמשים. אם ערך ה-INP של דף מסוים נמצא ברף 'טוב' – כלומר 200 אלפיות השנייה או פחות – אפשר לומר שהדף מגיב באופן מהימן לאינטראקציות של המשתמשים איתו.
משך הזמן שחולף עד להפעלת קריאות חזרה (callbacks) של אירועים בתגובה לאינטראקציה של משתמש יכול להשפיע באופן משמעותי על זמן האחזור הכולל של האינטראקציה, אבל זה לא ההיבט היחיד של INP שצריך להביא בחשבון. המדד INP מתייחס גם למשך הזמן שחלף עד להצגת התגובה הבאה של האינטראקציה. זהו משך הזמן שחולף עד שהשלמת פעולת הרינדור הנדרשת כדי לעדכן את ממשק המשתמש בתגובה לאינטראקציה.
לגבי ResizeObserver
, הדבר חשוב כי קריאת החזרה (callback) שמופעלת על ידי מופע של ResizerObserver
מתרחשת לפני עבודת העיבוד. הסיבה לכך היא שצריך להביא בחשבון את העבודה שמתבצעת בקריאה החוזרת, כי סביר מאוד שהיא תחייב שינוי בממשק המשתמש.
חשוב לבצע כמה שפחות עיבוד בקריאה חוזרת (callback) של ResizeObserver
, כי עיבוד יתר עלול לגרום למצבים שבהם הדפדפן יתעכב בביצוע משימות חשובות. לדוגמה, אם לאינטראקציה כלשהי יש קריאה חוזרת (callback) שגורמת להפעלה של קריאה חוזרת מסוג ResizeObserver
, חשוב לוודא שאתם מבצעים את הפעולות הבאות כדי לספק את חוויית השימוש הטובה ביותר:
- חשוב לוודא שהסלקטורים ב-CSS יהיו פשוטים ככל האפשר כדי להימנע מעבודה מיותרת של חישוב מחדש של סגנונות. חישובים מחדש של סגנונות מתבצעים ממש לפני הפריסה, וסלקטורים מורכבים של CSS עלולים לעכב פעולות פריסה.
- מומלץ להימנע מביצוע פעולות בקריאה החוזרת (callback) של
ResizeObserver
שעלולות לגרום לזרימות אוטומטיות חוזרות. - בדרך כלל, הזמן הנדרש לעדכון הפריסה של דף עולה ככל שמספר רכיבי ה-DOM בדף גדל. זה נכון גם אם הדפים משתמשים ב-
ResizeObserver
וגם אם לא, אבל ככל שהמורכבות המבנית של הדף גדלה, כך העבודה שמתבצעת בקריאה החוזרת (callback) שלResizeObserver
עשויה להיות משמעותית יותר.
סיכום
ResizeObserver
זמין בכל הדפדפנים העיקריים, והוא מספק דרך יעילה למעקב אחרי שינוי הגודל של רכיבים ברמת הרכיב. רק חשוב להיזהר לא לעכב את העיבוד יותר מדי באמצעות ממשק ה-API החזק הזה.