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