تتيح لك ResizeObserver معرفة متى يتغير حجم أحد العناصر.
قبل ResizeObserver، كان عليك ربط أداة معالجة بالحدث resize في المستند لتلقّي إشعار بأي تغيير في أبعاد إطار العرض. في معالج الأحداث، عليك بعد ذلك تحديد العناصر التي تأثّرت بهذا التغيير واستدعاء روتين معيّن للردّ بشكل مناسب. إذا كنت بحاجة إلى معرفة الأبعاد الجديدة لعنصر ما بعد تغيير حجمه، كان عليك استدعاء getBoundingClientRect() أو getComputedStyle()، ما قد يؤدي إلى حدوث مشكلة في التخطيط إذا لم تحرص على تجميع جميع عمليات القراءة وجميع عمليات الكتابة.
ولم يشمل ذلك حتى الحالات التي تغيّر فيها العناصر حجمها بدون تغيير حجم النافذة الرئيسية. على سبيل المثال، يمكن أن يؤدي إلحاق عناصر فرعية جديدة أو ضبط نمط display لعنصر على none أو إجراءات مشابهة إلى تغيير حجم عنصر أو العناصر الشقيقة أو العناصر الأصلية.
لهذا السبب، ResizeObserver هو نوع أساسي مفيد. وتتفاعل مع التغييرات في حجم أي من العناصر المرصودة، بغض النظر عن سبب التغيير.
ويوفّر أيضًا إمكانية الوصول إلى الحجم الجديد للعناصر التي تتم مراقبتها.
واجهة برمجة التطبيقات
تتشارك جميع واجهات برمجة التطبيقات التي تتضمّن اللاحقة 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. مربّع المحتوى هو المربّع الذي يمكن وضع المحتوى فيه. وهو
مربّع الحدود ناقص المساحة المتروكة.
من المهم ملاحظة أنّه على الرغم من أنّ ResizeObserver تسجّل كلاً من أبعاد contentRect ومساحة الحشو، إلا أنّها تراقب contentRect فقط.
لا تخلط بين contentRect والمربّع المحيط بالعنصر. المربّع المحيط، كما يظهر في getBoundingClientRect()، هو المربّع الذي يحتوي على العنصر بأكمله والعناصر التابعة له. تشكّل ملفات SVG استثناءً لهذه القاعدة، إذ تعرض
ResizeObserver أبعاد مربّع الإحاطة.
اعتبارًا من الإصدار 84 من Chrome، يتضمّن ResizeObserverEntry ثلاث خصائص جديدة لتوفير معلومات أكثر تفصيلاً. تعرض كل سمة من هذه السمات عنصر ResizeObserverSize يحتوي على السمتَين blockSize وinlineSize. تتعلّق هذه المعلومات بالعنصر الذي تم رصده في وقت استدعاء دالة ردّ الاتصال.
borderBoxSizecontentBoxSizedevicePixelContentBoxSize
تعرض كل هذه العناصر مصفوفات للقراءة فقط، لأنّه من المؤمّل أن تتوافق في المستقبل مع العناصر التي تتضمّن أجزاء متعددة، والتي تظهر في سيناريوهات الأعمدة المتعددة. في الوقت الحالي، لن تحتوي هذه المصفوفات إلا على عنصر واحد.
يتوفّر عدد محدود من الأنظمة الأساسية التي تتوافق مع هذه الخصائص، ولكن يتوافق 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 في جميع المتصفحات الرئيسية وتوفّر طريقة فعّالة لتتبُّع عمليات تغيير حجم العناصر على مستوى العنصر. يجب توخّي الحذر من عدم تأخير العرض بشكل كبير عند استخدام واجهة برمجة التطبيقات الفعّالة هذه.