يُعلمك الخيار 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
. تتناول هذه
المعلومات العنصر الذي تم رصده في وقت استدعاء دالة ردّ الاتصال.
borderBoxSize
contentBoxSize
devicePixelContentBoxSize
تعرض كل هذه العناصر صفائف للقراءة فقط لأنّه من المرجّح أن تتمكّن في المستقبل من معالجة العناصر التي تحتوي على أجزاء متعددة، والتي تحدث في سيناريوهات الأعمدة المتعددة. في الوقت الحالي، ستتضمّن هذه الصفائف عنصرًا واحدًا فقط.
إنّ توافق هذه السمات مع المنصات محدود، ولكن Firefox يتوافق مع السمتَين الأوليتَين.
متى يتم الإبلاغ عن هذه الرسائل؟
تفرض المواصفة أن يعالج ResizeObserver
جميع أحداث تغيير الحجم
قبل الرسم وبعد التخطيط. وهذا يجعل طلب إعادة الاتصال بـ ResizeObserver
هو
المكان المثالي لإجراء تغييرات على تنسيق صفحتك. وبما أنّ ResizeObserver
تتم معالجة
العناصر بين التنسيق والرسم، سيؤدي ذلك إلى إبطال
التنسيق فقط، وليس الرسم.
Gotcha
قد تتساءل: ماذا يحدث إذا غيّرتُ حجم أحد العناصر المرصودة
داخل دالة الاستدعاء إلى 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 أيضًا في الاعتبار مقدار الوقت الذي يستغرقه العرض التالي للتفاعل. يشير ذلك إلى الوقت المستغرَق لإكمال عمل المعالجة المطلوب لتعديل واجهة المستخدِم استجابةً للتفاعل.
في ما يتعلّق بـ ResizeObserver
، هذا مهم لأنّ دالة الاستدعاء التي
تُنفّذ مثيل ResizerObserver
تحدث قبل بدء عملية التقديم مباشرةً. ويعود سبب ذلك إلى أنّه يجب مراعاة العمل الذي يتم تنفيذه في دالة ردّ الاتصال، لأنّ نتيجة هذا العمل قد تتطلّب على الأرجح إجراء تغيير على واجهة المستخدم.
احرص على تنفيذ أقل قدر ممكن من عمليات المعالجة المطلوبة في ResizeObserver
الاستدعاء، لأنّ عمليات المعالجة المفرطة يمكن أن تؤدي إلى حدوث حالات يتأخّر فيها المتصفّح عن تنفيذ مهام مهمة. على سبيل المثال، إذا كان أي تفاعل يحتوي على
طلب استدعاء يؤدي إلى تنفيذ طلب استدعاء ResizeObserver
، تأكَّد من تنفيذ الخطوات التالية
لتسهيل تجربة سلسة قدر الإمكان:
- تأكَّد من أنّ أدوات اختيار CSS بسيطة قدر الإمكان من أجل تجنُّب زيادة عمل إعادة احتساب الأنماط. تحدث عمليات إعادة احتساب الأنماط قبل التنسيق مباشرةً، ويمكن أن تؤدّي أدوات اختيار لغة CSS المعقّدة إلى تأخير عمليات التنسيق.
- تجنَّب تنفيذ أيّ عمل في
ResizeObserver
دالة الاستدعاء التي يمكن أن تؤدي إلى عمليات إعادة تدفق قسري. - يزداد الوقت اللازم لتعديل تنسيق الصفحة بشكل عام مع
عدد عناصر DOM في الصفحة. على الرغم من أنّ هذا صحيح سواء كانت الصفحات تستخدم
ResizeObserver
أم لا، يمكن أن يصبح العمل الذي يتم تنفيذه في دالة callback الخاصة بـResizeObserver
ملحوظًا مع زيادة التعقيد الهيكلي للصفحة.
الخاتمة
يتوفّر ResizeObserver
في جميع المتصفّحات
الكبرى
ويوفّر طريقة فعّالة لرصد عمليات تغيير حجم العناصر على مستوى
العنصر. فقط احرِص على عدم تأخير العرض كثيرًا باستخدام واجهة برمجة التطبيقات هذه القوية.