ResizeObserver
به شما امکان می دهد از زمانی که اندازه یک عنصر تغییر می کند مطلع شوید.
قبل از ResizeObserver
، باید یک شنونده را به رویداد resize
سند متصل میکردید تا از هرگونه تغییر در ابعاد ویوپورت مطلع شوید. در کنترل کننده رویداد، باید بفهمید کدام عناصر تحت تأثیر آن تغییر قرار گرفته اند و یک روال خاص را برای واکنش مناسب فراخوانی کنید. اگر بعد از تغییر اندازه به ابعاد جدید یک عنصر نیاز داشتید، باید getBoundingClientRect()
یا getComputedStyle()
فراخوانی کنید، که اگر مراقب نباشید که همه خواندهها و همه نوشتههای خود را دستهبندی کنید، میتواند باعث thrash شدن طرحبندی شود.
این حتی مواردی را که عناصر بدون تغییر اندازه پنجره اصلی تغییر اندازه میدهند، پوشش نمیدهد. به عنوان مثال، اضافه کردن فرزندان جدید، تنظیم سبک display
عنصر روی none
، یا اقدامات مشابه میتواند اندازه یک عنصر، خواهر و برادر یا اجداد آن را تغییر دهد.
به همین دلیل است که ResizeObserver
یک بدوی مفید است. به تغییرات اندازه هر یک از عناصر مشاهده شده، مستقل از آنچه باعث تغییر شده است، واکنش نشان می دهد. دسترسی به اندازه جدید عناصر مشاهده شده را نیز فراهم می کند.
API
همه APIهای دارای پسوند Observer
که در بالا ذکر کردیم، یک طراحی API ساده دارند. ResizeObserver
نیز از این قاعده مستثنی نیست. شما یک شی ResizeObserver
ایجاد میکنید و یک 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
و padding را گزارش می کند ، فقط contentRect
را تماشا می کند . contentRect
با کادر محدود عنصر اشتباه نگیرید . جعبه مرزی، همانطور که توسط getBoundingClientRect()
گزارش شده است، کادری است که کل عنصر و فرزندان آن را در بر می گیرد. SVG ها استثنایی از این قاعده هستند، جایی که ResizeObserver
ابعاد کادر محدود را گزارش می کند.
از Chrome 84، ResizeObserverEntry
دارای سه ویژگی جدید برای ارائه اطلاعات دقیق تر است. هر یک از این ویژگی ها یک شی ResizeObserverSize
حاوی یک ویژگی blockSize
و یک ویژگی inlineSize
را برمی گرداند. این اطلاعات مربوط به عنصر مشاهده شده در زمان فراخوانی تماس است.
-
borderBoxSize
-
contentBoxSize
-
devicePixelContentBoxSize
همه این موارد آرایههای فقط خواندنی را برمیگردانند زیرا در آینده امید میرود که بتوانند از عناصری پشتیبانی کنند که دارای چند قطعه هستند که در سناریوهای چند ستونی رخ میدهند. در حال حاضر، این آرایه ها فقط شامل یک عنصر خواهند بود.
پشتیبانی پلت فرم برای این ویژگی ها محدود است، اما فایرفاکس در حال حاضر از دو مورد اول پشتیبانی می کند .
چه زمانی گزارش می شود؟
این مشخصات منع می کند که 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)
Interaction to Next Paint (INP) معیاری است که پاسخگویی کلی یک صفحه به تعاملات کاربر را می سنجد. اگر INP یک صفحه در آستانه "خوب" باشد - یعنی 200 میلی ثانیه یا کمتر - می توان گفت که یک صفحه به طور قابل اعتمادی به تعاملات کاربر با آن پاسخ می دهد.
در حالی که مدت زمانی که طول می کشد تا تماس های رویداد در پاسخ به تعامل کاربر اجرا شود، می تواند به طور قابل توجهی به تأخیر کلی یک تعامل کمک کند، این تنها جنبه INP نیست که باید در نظر گرفته شود. INP همچنین مدت زمان لازم برای رخ دادن رنگ بعدی تعامل را در نظر می گیرد. این مقدار زمانی است که طول می کشد تا کار رندر مورد نیاز برای به روز رسانی رابط کاربری در پاسخ به یک تعامل کامل شود.
در مورد ResizeObserver
، این مهم است زیرا تماسی که یک نمونه ResizerObserver
اجرا میکند درست قبل از رندر کردن کار انجام میشود. این به دلیل طراحی است، زیرا کاری که در تماس برگشتی رخ می دهد باید در نظر گرفته شود، زیرا نتیجه آن کار به احتمال زیاد نیاز به تغییر در رابط کاربری دارد.
مراقب باشید که در یک فراخوان ResizeObserver
کار رندر کمتری را که لازم است انجام دهید، زیرا کار رندر بیش از حد می تواند موقعیت هایی را ایجاد کند که مرورگر در انجام کارهای مهم تاخیر داشته باشد. برای مثال، اگر هر فعل و انفعالی دارای تماس برگشتی است که باعث میشود یک ResizeObserver
پاسخ داده شود، مطمئن شوید که کارهای زیر را انجام میدهید تا راحتترین تجربه ممکن را تسهیل کنید:
- اطمینان حاصل کنید که انتخابگرهای CSS شما تا حد امکان ساده هستند تا از محاسبه مجدد سبک بیش از حد جلوگیری کنید . محاسبات مجدد سبک درست قبل از طرحبندی اتفاق میافتد و انتخابگرهای پیچیده CSS میتوانند عملیات طرحبندی را به تأخیر بیندازند.
- از انجام هر کاری در پاسخ به تماس
ResizeObserver
خود که میتواند باعث جریانهای مجدد اجباری شود، خودداری کنید. - زمان لازم برای بهروزرسانی طرحبندی صفحه معمولاً با تعداد عناصر DOM در یک صفحه افزایش مییابد. در حالی که این درست است چه صفحاتی از
ResizeObserver
استفاده کنند یا نه، کار انجام شده در پاسخ به تماسResizeObserver
می تواند با افزایش پیچیدگی ساختاری صفحه قابل توجه باشد.
نتیجه گیری
ResizeObserver
در همه مرورگرهای اصلی موجود است و روشی کارآمد برای نظارت بر تغییر اندازه عناصر در سطح عنصر ارائه می دهد. فقط مراقب باشید با این API قدرتمند، رندرینگ را بیش از حد به تاخیر نیندازید.