ResizeObserver: برای عناصر مانند document.onresize است

ResizeObserver به شما امکان می دهد از زمانی که اندازه یک عنصر تغییر می کند مطلع شوید.

قبل از ResizeObserver ، باید یک شنونده را به رویداد resize سند متصل می‌کردید تا از هرگونه تغییر در ابعاد ویوپورت مطلع شوید. در کنترل کننده رویداد، باید بفهمید کدام عناصر تحت تأثیر آن تغییر قرار گرفته اند و یک روال خاص را برای واکنش مناسب فراخوانی کنید. اگر بعد از تغییر اندازه به ابعاد جدید یک عنصر نیاز داشتید، باید getBoundingClientRect() یا getComputedStyle() را فراخوانی کنید، که اگر مراقب نباشید که همه خوانده‌ها و همه نوشته‌های خود را دسته‌بندی کنید، می‌تواند باعث thrash شدن طرح‌بندی شود.

این حتی مواردی را که عناصر بدون تغییر اندازه پنجره اصلی تغییر اندازه می‌دهند، پوشش نمی‌دهد. به عنوان مثال، اضافه کردن فرزندان جدید، تنظیم سبک display عنصر روی none ، یا اقدامات مشابه می‌تواند اندازه یک عنصر، خواهر و برادر یا اجداد آن را تغییر دهد.

به همین دلیل است که ResizeObserver یک بدوی مفید است. به تغییرات اندازه هر یک از عناصر مشاهده شده، مستقل از آنچه باعث تغییر شده است، واکنش نشان می دهد. دسترسی به اندازه جدید عناصر مشاهده شده را نیز فراهم می کند.

پشتیبانی مرورگر

  • 64
  • 79
  • 69
  • 13.1

منبع

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 را برمی‌گرداند. جعبه محتوا جعبه ای است که می توان محتوا را در آن قرار داد. این جعبه حاشیه منهای بالشتک است.

نموداری از مدل جعبه CSS.

توجه به این نکته مهم است که در حالی که 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 قدرتمند، رندرینگ را بیش از حد به تاخیر نیندازید.