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

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

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

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

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

Browser Support

  • کروم: ۶۴.
  • لبه: ۷۹.
  • فایرفاکس: ۶۹.
  • سافاری: ۱۳.۱.

Source

رابط برنامه‌نویسی کاربردی

تمام APIهایی که پسوند Observer دارند و در بالا به آنها اشاره کردیم، یک طراحی API ساده دارند. ResizeObserver نیز از این قاعده مستثنی نیست. شما یک شیء ResizeObserver ایجاد می‌کنید و یک تابع فراخوانی (callback) به سازنده (constructor) ارسال می‌کنید. این تابع فراخوانی، آرایه‌ای از اشیاء 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 و هم فاصله‌ی بین عناصر را گزارش می‌دهد ، اما فقط contentRect زیر نظر دارد . contentRect با کادر اطراف عنصر اشتباه نگیرید . کادر اطراف، همانطور که توسط getBoundingClientRect() گزارش می‌شود، کادری است که شامل کل عنصر و فرزندان آن می‌شود. SVGها از این قاعده مستثنی هستند و ResizeObserver ابعاد کادر اطراف را گزارش می‌دهد.

از کروم ۸۴، ResizeObserverEntry سه ویژگی جدید برای ارائه اطلاعات دقیق‌تر دارد. هر یک از این ویژگی‌ها یک شیء ResizeObserverSize را برمی‌گرداند که شامل یک ویژگی blockSize و یک ویژگی inlineSize است. این اطلاعات مربوط به عنصر مشاهده‌شده در زمان فراخوانی تابع فراخوانی است.

  • borderBoxSize
  • contentBoxSize
  • devicePixelContentBoxSize

همه این آیتم‌ها آرایه‌های فقط خواندنی برمی‌گردانند، زیرا امید است در آینده بتوانند از عناصری که دارای چندین قطعه کد هستند، که در سناریوهای چند ستونی رخ می‌دهد، پشتیبانی کنند. در حال حاضر، این آرایه‌ها فقط شامل یک عنصر خواهند بود.

پشتیبانی پلتفرم برای این ویژگی‌ها محدود است، اما فایرفاکس از قبل از دو مورد اول پشتیبانی می‌کند .

چه زمانی گزارش می‌شود؟

این مشخصات، پردازش تمام رویدادهای تغییر اندازه را قبل از رسم و بعد از طرح‌بندی ResizeObserver ممنوع می‌کند. این امر، فراخوانی ResizeObserver را به مکانی ایده‌آل برای ایجاد تغییرات در طرح‌بندی صفحه شما تبدیل می‌کند. از آنجا که پردازش ResizeObserver بین طرح‌بندی و رسم اتفاق می‌افتد، انجام این کار فقط طرح‌بندی را نامعتبر می‌کند، نه رنگ‌آمیزی را.

گوچا

ممکن است از خود بپرسید: اگر اندازه یک عنصر مشاهده شده را در داخل تابع فراخوانی 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 میلی‌ثانیه یا کمتر - می‌توان گفت که آن صفحه به طور قابل اعتمادی به تعاملات کاربر با آن پاسخگو است.

اگرچه مدت زمانی که طول می‌کشد تا فراخوانی‌های رویداد در پاسخ به یک تعامل کاربر اجرا شوند، می‌تواند به طور قابل توجهی در تأخیر کل یک تعامل نقش داشته باشد، اما این تنها جنبه INP نیست که باید در نظر گرفته شود. INP همچنین مدت زمانی را که طول می‌کشد تا رنگ‌آمیزی بعدی تعامل رخ دهد، در نظر می‌گیرد. این مدت زمانی است که برای تکمیل کار رندر مورد نیاز برای به‌روزرسانی رابط کاربری در پاسخ به یک تعامل لازم است.

در مورد ResizeObserver ، این موضوع مهم است زیرا فراخوانی که یک نمونه ResizerObserver اجرا می‌کند، درست قبل از رندر کردن کار رخ می‌دهد. این به دلیل طراحی آن است، زیرا کاری که در فراخوانی انجام می‌شود باید در نظر گرفته شود، زیرا نتیجه آن کار به احتمال زیاد نیاز به تغییر در رابط کاربری خواهد داشت.

مراقب باشید که در فراخوانی ResizeObserver ، تا حد امکان کار رندرینگ کمی انجام دهید، زیرا کار رندرینگ بیش از حد می‌تواند موقعیت‌هایی را ایجاد کند که مرورگر در انجام کارهای مهم با تأخیر مواجه شود. برای مثال، اگر تعاملی دارای فراخوانی است که باعث اجرای فراخوانی ResizeObserver می‌شود، مطمئن شوید که موارد زیر را برای تسهیل روان‌ترین تجربه ممکن انجام می‌دهید:

  • مطمئن شوید که انتخابگرهای CSS شما تا حد امکان ساده هستند تا از کار بیش از حد محاسبه مجدد سبک جلوگیری شود . محاسبات مجدد سبک درست قبل از طرح‌بندی رخ می‌دهد و انتخابگرهای پیچیده CSS می‌توانند عملیات طرح‌بندی را به تأخیر بیندازند.
  • از انجام هر کاری در فراخوانی ResizeObserver که می‌تواند باعث ایجاد جریان‌های اجباری شود، خودداری کنید.
  • زمان مورد نیاز برای به‌روزرسانی طرح‌بندی یک صفحه معمولاً با افزایش تعداد عناصر DOM در یک صفحه افزایش می‌یابد. اگرچه این موضوع چه صفحات ResizeObserver استفاده کنند و چه نکنند، صادق است، اما با افزایش پیچیدگی ساختاری یک صفحه، کار انجام شده در یک فراخوانی ResizeObserver می‌تواند قابل توجه شود.

نتیجه‌گیری

ResizeObserver در تمام مرورگرهای اصلی موجود است و روشی کارآمد برای نظارت بر تغییر اندازه عناصر در سطح عنصر ارائه می‌دهد. فقط مراقب باشید که رندر کردن با این API قدرتمند را بیش از حد به تأخیر نیندازید.