ResizeMonitorer: إنها مثل document.onresize للعناصر

يُعلمك الخيار ResizeObserver عند تغيير حجم عنصر.

قبل ResizeObserver، كان عليك إرفاق مستمع بحدث resize للحصول على إشعار بشأن أي تغيير في سمات مساحة العرض. في معالج الحدث، عليك بعد ذلك تحديد العناصر التي تأثّرت بهذا التغيير واستدعاء روتين معيّن للتفاعل بشكل مناسب. إذا كنت بحاجة إلى معرفة السمات الجديدة لعنصر بعد تغيير حجمه، كان عليك استدعاء getBoundingClientRect() أو getComputedStyle()، ما قد يؤدي إلى ازدحام العرض إذا لم تهتم بتجميع كل عمليات القراءة وكل عمليات الكتابة.

ولم يشمل ذلك الحالات التي تغيّر فيها حجم العناصر بدون تغيير حجم النافذة الرئيسية. على سبيل المثال، يمكن أن يؤدي إلحاق عناصر فرعية جديدة أو ضبط نمط display للعنصر على none أو إجراءات مشابهة إلى تغيير حجم العنصر أو عناصره الشقيقة أو عناصره الأصلية.

هذا هو السبب في أن ResizeObserver هو أساس مفيد. ويتفاعل مع التغييرات في حجم أيّ من العناصر المرصودة، بغض النظر عن سبب التغيير. ويتيح أيضًا الوصول إلى الحجم الجديد للعناصر المرصودة.

توافق المتصفّح

  • Chrome: 64
  • ‫Edge: 79
  • Firefox: 69.
  • ‫Safari: 13.1

المصدر

واجهة برمجة التطبيقات

تشترك جميع واجهات برمجة التطبيقات التي تحمل اللاحقة Observer التي ذكرناها أعلاه في تصميم بسيط لواجهة برمجة التطبيقات. ولا يُستثنى من ذلك ResizeObserver. تنشئ كائن ResizeObserver وتمرر استدعاء إلى الدالة الإنشائية. يتمّ تمرير مصفوفة من عناصر 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 أبعاد مربّع الإحاطة.

بدءًا من إصدار Chrome 84، أصبح لـ ResizeObserverEntry ثلاث سمات جديدة لتوفير معلومات أكثر تفصيلاً. تعرض كل من هذه السمات كائن ResizeObserverSize يحتوي على السمة blockSize والسمة inlineSize. تتعلق هذه المعلومات بالعنصر الذي يتم ملاحظته في وقت استدعاء رد الاتصال.

  • borderBoxSize
  • contentBoxSize
  • devicePixelContentBoxSize

تعرض كل هذه العناصر صفائف للقراءة فقط لأنه من المأمول في المستقبل أن تدعم العناصر التي تحتوي على أجزاء متعددة، والتي تحدث في سيناريوهات متعددة الأعمدة. في الوقت الحالي، ستتضمّن هذه الصفائف عنصرًا واحدًا فقط.

إنّ توافق هذه السمات مع المنصات محدود، ولكن Firefox يتوافق مع السمتَين الأوليتَين.

متى يتم الإبلاغ عن هذه الرسائل؟

تفرض المواصفة أن يعالج 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 في جميع المتصفّحات الكبرى ويوفّر طريقة فعّالة لرصد عمليات تغيير حجم العناصر على مستوى العنصر. فقط احرِص على عدم تأخير العرض كثيرًا باستخدام واجهة برمجة التطبيقات هذه القوية.