content-visibility: عرض خارج الشاشة دون افتراض قوائم بأكملها (virtualization)
content-visibility: عرض خارج الشاشة دون افتراض قوائم بأكملها (virtualization)
قائمة من ألف تعليق أسفل مقال تُعرَض ببطء ملحوظ، والتمرير خلالها يتقطّع على هاتف أضعف. الحلّ المعتاد في React هو الافتراض (virtualization) – react-window أو @tanstack/react-virtual – الذي لا يعرض في DOM سوى العناصر التي تقع فعليًا ضمن النافذة المرئية، بالإضافة إلى هامش صغير. يعمل هذا، لكن له كلفة نادرًا ما يُتحدَّث عنها عند التطبيق: العناصر خارج النافذة ليست مخفيّة، بل غير موجودة في DOM. الضغط على Ctrl+F لن يجد نصًّا في التعليق رقم 850 قبل أن تُمرِّر إليه يدويًا. طباعة الصفحة ستُظهر فقط ما كان معروضًا فعليًا لحظة استدعاء الطباعة. وتنقّل قارئ الشاشة عبر العناوين أو المعالم (landmarks) يتجاوز محتوى غير موجود فعليًا في الشجرة – وهذا ليس خللًا في الافتراض، بل نتيجة حتمية له.
content-visibility تهاجم مشكلة الأداء نفسها من زاوية مختلفة تمامًا: تبقى العناصر في DOM، دائمًا، كاملةً. يكتفي المتصفح بتجاهل العمل الذي كان سيضطرّ عادة إلى تنفيذه لها – التخطيط، والطلاء، وإنشاء طبقات التركيب – إلى أن يصبح العنصر قريبًا بما يكفي من المنطقة المرئية بحيث يصبح لهذا العمل معنى.
ما الذي تعنيه فعليًا عبارة «تجاهل العمل»
خاصية content-visibility: auto على عنصر ما تُلزم المتصفح بتطبيق الاحتواء في CSS (CSS Containment) عليه – الآلية نفسها تمامًا التي مكّنت، في مقال Container Queries، من تجنّب الحلقة المفرغة عند استعلام حجم الحاوية. هنا يخدم الاحتواء غرضًا مختلفًا: إن كان العنصر خارج الشاشة، يستطيع المتصفح أن يفترض بأمان أن تخطيطه وطلاءه الداخليين لا يؤثران في شيء خارج نطاقه هو، فيمكنه تأجيلهما بالكامل، دون حساب بكسل واحد بالداخل.
css
.comment {content-visibility: auto;contain-intrinsic-size: auto 180px;}
contain-intrinsic-size ليست إضافة تجميلية – فمن دونها سيتقلّص العنصر المُتجاهَل إلى ارتفاع صفري، لأن المتصفح لا يملك مصدرًا لحجمه ما دام لا يحسب التخطيط بالداخل. هذه المشكلة نفسها تمامًا التي تظهر مع container-type: size دون ارتفاع صريح – إلا أن النتيجة هنا ليست بطاقة تختفي، بل شريط تمرير يقفز، لأن ارتفاع المستند يتغيّر فجأة كلّما دخل تعليق آخر وضع التجاهل أو خرج منه. القيمة auto 180px تخبر المتصفح بأمرين معًا: استخدم 180px كتقدير أوّلي، قبل أن يُعرَض العنصر فعليًا ولو مرّة واحدة، ثم تذكّر حجمه الفعلي المحسوب واستخدم هذه القيمة المحفوظة كبديل مؤقّت (placeholder) عند كل تجاهل لاحق – فإذا كانت التعليقات الحقيقية متفاوتة الارتفاع، سيزداد تخمين المتصفح دقّةً مع الوقت لمقدار المساحة الواجب حجزها، بدلًا من التمسّك بقيمة واحدة ثابتة.
حيلة لا تحصل عليها مع الافتراض الكامل: «مخفي حتى يُعثر عليه» (hidden until found)
هنا تفعل content-visibility أمرًا لا يستطيع الافتراض فعله من حيث المبدأ – لا من حيث الممارسة فقط. العنصر في وضع auto، رغم تجاهله في العرض، لا يزال نصّه موجودًا فعليًا في DOM. يعرف المتصفح ذلك ويعامل هذا العنصر بوصفه «مخفيًّا لكن قابلًا للمطابقة» (hidden but matchable): حين يضغط المستخدم Ctrl+F ويكتب عبارة تطابق نصًّا داخل عنصر متجاهَل، يتراجع المتصفح مؤقتًا عن التجاهل، ويعرضه فعليًا، ويُبرز موضع التطابق، ويُمرِّر إليه – تلقائيًا، دون أي كود من جانبك. display: none لم يفعل هذا يومًا (Ctrl+F يتجاهله ببساطة)، والقائمة المُفترَضة لا يمكنها فعل ذلك، لأن النص الذي تبحث عنه غير موجود أصلًا في DOM قبل أن تُمرِّر إليه يدويًا.
مثال عملي: قائمة تعليقات دون سطر واحد من JS للافتراض
jsx
const CommentList = ({ comments }) => (<ul className="comment-list">{comments.map((comment) => (<li key={comment.id} className="comment"><p className="comment__author">{comment.author}</p><p className="comment__body">{comment.body}</p></li>))}</ul>);export default CommentList;
css
.comment {content-visibility: auto;contain-intrinsic-size: auto 180px;padding: 12px 0;border-bottom: 1px solid #e5e5e5;}
يعرض المكوّن بأكمله جميع عناصر <li> الألف في DOM – دون أي منطق نافذة تمرير، ودون أي حساب لتحديد العناصر المرئية حاليًا، ودون أي مكتبة. يقرّر المتصفح بنفسه أيّ التعليقات من بين الألف يستحق حساب تخطيطه وطلائه في تلك اللحظة، وأيّها «غير موجود» عرضيًّا، رغم وجوده كاملًا في DOM.
متى لا يكفي هذا – ولماذا لا يزال الافتراض منطقيًا
تحلّ content-visibility كلفة العرض – التخطيط والطلاء. لكنها لا تحلّ الكلفة التي غالبًا ما تؤلم أكثر في تطبيق React: كلّ عنصر من الألف <li> لا يزال مكوّن React حقيقيًّا، يُركَّب (mount)، ويُرطَّب (hydrate)، وقد تكون له خطافاته (hooks) وتأثيراته (effects) الخاصة. تجاهل العرض على مستوى CSS لا يجعل هذه الآلاف من نُسخ المكوّنات تتوقف عن الوجود في شجرة React، ولا يُلغي كلفة جافاسكريبت اللازمة لإنشائها. بالنسبة للكتل النصية البسيطة والثابتة غالبًا (تعليقات، فقرات مقال طويل، صفوف جدول دون تفاعلية) لا يهمّ هذا كثيرًا – فالكلفة الحقيقية كانت أصلًا في التخطيط والطلاء، لا في منطق المكوّن. أمّا في القوائم المكوّنة من مكوّنات ثقيلة وتفاعلية – لكلّ منها حالته الخاصة، واشتراكاته، وعرضه المكلف – فالافتراض الحقيقي، الذي يزيل النُّسخ غير المستخدَمة من شجرة React تمامًا، يظل أحيانًا الطريقة الوحيدة للحفاظ على السلاسة.
الفخاخ والممارسات الجيدة
contain-intrinsic-sizeإلزامية عمليًا، لا اختيارية. إغفالها يؤدي إلى قفز شريط التمرير وارتفاع خاطئ للمستند عند كل انتقال للعنصر إلى وضع التجاهل والعودة منه.content-visibility: hiddenليست مثلauto. خاصيةhiddenتتجاهل العرض دائمًا، بصرف النظر عن موضع العنصر على الشاشة، لكن خلافًا لـdisplay: noneفإنها تحتفظ بالحالة الداخلية للتخطيط المحسوب في ذاكرة تخزين مؤقتة – فإعادة إظهار العنصر (كتبديل تبويب مثلًا) أرخص ممّا هي عليه بعدdisplay: none، لأن المتصفح لا يحسب كل شيء من الصفر. العنصر ذوcontent-visibility: hiddenيُزال بشكل صحيح أيضًا من شجرة إتاحة الوصول، تمامًا مثلdisplay: none– وهو خيار آمن للتبويبات واللوحات التي تُبدَّل كثيرًا، لا مجرّد حيلة أداء.- ليست بديلًا عن الافتراض للقوائم ذات المكوّنات الثقيلة. عاملها كأداة أولى وأرخص للقوائم البسيطة والثابتة غالبًا – ولا تلجأ إلى الافتراض الحقيقي إلا حين يُظهر أداة التنميط (profiler) فعليًا كلفة على مستوى JS/React، لا على مستوى التخطيط وحده.
- تحقّق من الدعم قبل الاعتماد الكامل على «مخفي لكن قابل للمطابقة». خاصية
content-visibility: autoنفسها تتمتع اليوم بدعم واسع في جميع المحركات الرئيسية، لكن سلوك «الكشف المؤقت» عند Ctrl+F يكون غالبًا أنضج ما يكون في Chromium – اعتبره ميزة إضافية جيدة، لا ضمانة تبني عليها متطلبًا في إتاحة الوصول.
الخلاصة
يحلّ الافتراض وcontent-visibility للوهلة الأولى المشكلة نفسها – قائمة طويلة تُعرَض ببطء زائد – لكنهما يفعلان ذلك على مستويين مختلفين تمامًا. الافتراض يزيل العناصر من DOM ومن شجرة React كليًّا، ويدفع ثمن ذلك خسارةً في Ctrl+F والطباعة وجزء من تنقّل إتاحة الوصول. أمّا content-visibility فتترك DOM كاملًا وتُلزم المتصفح بتجاهل العمل المكلف في العرض فقط لما هو غير مرئيّ حاليًا – في المقابل تحصل على تحسين أقلّ عدوانية، لكن مجّانًا، دون كسر أي شيء كان المتصفح أصلًا قادرًا على فعله مع المستند الكامل. بالنسبة لمعظم القوائم الطويلة والبسيطة نسبيًّا، هذه هي الأداة الصحيحة الأولى – ويبقى الافتراض احتياطيًّا للحالات التي لا تكفي فيها فعليًّا.