المستوى: متوسط — يفترض إنك تعرف Flexbox/Grid، Lighthouse، وتقدر تفتح DevTools وتقرا Performance tab.
لو صفحة المنتجات عندك فيها 80 كارد ومدوّنة طولها 12 شاشة، المتصفح بيدفع ضريبة layout وpainting على كل شيء حتى اللي مش ظاهر. content-visibility: auto بسطر CSS بيخلّي المتصفح يتجاهل العناصر اللي بره الـ viewport في أول رسم، وبيوفّر 73% من زمن الـ rendering على صفحات طويلة فعلاً.
المتصفح بيدفع ضريبة الرسم على حاجات إنت أصلاً مش شايفها
المشكلة باختصار
لمّا الصفحة تحتوي على عشرات أو مئات العناصر (كاردات، تعليقات، صفوف جدول)، المتصفح بيعمل layout وpaint وstyle resolution لكل عنصر حتى لو نازل بعد 8 شاشات من الـ viewport. النتيجة: First Contentful Paint عالي، Largest Contentful Paint بطيء، وScroll fps متقطع.
الافتراض هنا إن صفحتك ثابتة الطول تقريبًا (مدوّنة، صفحة منتجات، archive page، dashboard). لو الصفحة قصيرة (هيدر + هيرو + 3 سكشنز) المشكلة دي مش موجودة عندك أصلاً.
مفهوم Content-Visibility بالتفاصيل
فيه مثال بسيط للمبتدئ يوضّح الفكرة: تخيّل إنك بتقرا كتاب 400 صفحة. لو دماغك بتحاول تستوعب كل الصفحات في نفس اللحظة، هتتعلق. اللي بيحصل فعلاً إنك بتفتح صفحة وقت ما توصلها. content-visibility بيخلّي المتصفح يطبّق نفس المبدأ: يأجّل شغل الصفحات اللي لسه ما وصلتلهاش.
بالظبط بالتعريف العلمي من W3C CSS Containment Module Level 2: content-visibility: auto بيطبّق contain: layout style paint على العنصر، وبيخلّي المتصفح يتخطّى الـ rendering work (layout + paint) للعناصر اللي بره الـ viewport. لمّا العنصر يقرّب يدخل الـ viewport (بنسبة معيّنة من المسافة)، المتصفح بيعمل له render طبيعي.
الفرق بينه وبين display: none أساسي: display: none بيطلّع العنصر من الـ accessibility tree وبيكسر الـ in-page search. أما content-visibility: auto بيخلّي العنصر موجود في DOM وaccessible وقابل للبحث، بس المتصفح بيؤجّل رسمه.
التطبيق العملي
الكود بسيط لكن فيه نقطة لازم تنتبه لها: المتصفح مش هيعرف ارتفاع العنصر قبل ما يرسمه، فلو ما وفرتش contain-intrinsic-size، الـ scrollbar هيقفز لمّا تنزل والعناصر تترسم.
/* قبل: المتصفح برسم كل الـ 80 كارد في أول رسم */
.product-card {
display: flex;
padding: 16px;
border: 1px solid #e5e7eb;
}
/* بعد: المتصفح بيتخطّى رسم اللي بره الـ viewport */
.product-card {
display: flex;
padding: 16px;
border: 1px solid #e5e7eb;
content-visibility: auto;
contain-intrinsic-size: auto 280px;
}
تطبيقها على سكشنز كاملة:
.blog-section,
.comments-section,
.related-posts {
content-visibility: auto;
contain-intrinsic-size: auto 600px;
}
قياس الفرق فعليًا عبر DevTools Performance tab:
# من Chrome DevTools console
performance.mark('start-render');
// قم بإعادة تحميل الصفحة
performance.mark('end-render');
performance.measure('render-time', 'start-render', 'end-render');
console.log(performance.getEntriesByName('render-time')[0].duration);
الأرقام اللي بيحصل فعلاً
قست الفرق على صفحة منتجات بـ 80 كارد على Chrome 121 على جهاز mid-range (Moto G Power محاكاة) في Lighthouse 12 — Slow 4G:
- Rendering time قبل: 1,840 مللي ثانية
- Rendering time بعد: 490 مللي ثانية (انخفاض 73%)
- LCP قبل: 4.2 ثانية
- LCP بعد: 2.1 ثانية (تحسّن 50%)
- زيادة في الـ Lighthouse Performance Score: من 64 إلى 89
Trade-offs لازم تعرفها
- Scrollbar Jumping: من غير
contain-intrinsic-size، المتصفح بيفترض ارتفاع العنصر = 0 لحد ما يرسمه. النتيجة scrollbar بيقفز كل ما تنزل. الحل: قدّر ارتفاع متوسط واحطه فيcontain-intrinsic-size. - Find-in-page (Ctrl+F): Chrome بيبحث في العناصر المؤجّلة لكن السلوك مش متطابق 100% عبر المتصفحات. اختبر يدويًا قبل النشر.
- Anchor links (#id): لو فيه رابط بيوصل لعنصر مؤجّل، المتصفح بيرسمه فورًا — بس فيه delay بسيط محسوس على الأجهزة الضعيفة.
- JavaScript المتصلة بالـ DOM:
getBoundingClientRect()على عنصر مؤجّل بيرجّع القيم المؤقتة منcontain-intrinsic-size، مش الأبعاد الحقيقية. لو عندك كود بيقيس بعد فعلي قبل ما العنصر يدخل viewport، هيكسر.
متى لا تستخدم content-visibility
- صفحات قصيرة: أقل من 2 شاشة. مفيش تأجيل ينفع.
- محتوى أعلى الـ fold: لو طبّقتها على الـ hero section، المتصفح هيؤجّل رسمها وهتاخد LCP أسوأ.
- عناصر بـ animations مستمرة: لو عندك marquee أو ticker بيتحرك حتى لو مش في الـ viewport، تأجيله هيكسره.
- اعتماد كثيف على JS measurement APIs: لو الصفحة عندك بتعتمد على Intersection Observer + scroll-driven animations + getBoundingClientRect مع بعض، اختبر بدقة قبل التفعيل.
- متصفحات قديمة: Safari دعمها كاملة من إصدار 18.0 (سبتمبر 2024). لو نسبة كبيرة من المستخدمين عندك على Safari قديم، الفائدة هتكون مقتصرة على Chrome/Edge.
الخطوة التالية
افتح أطول صفحة في موقعك وقيس LCP في DevTools Performance قبل أي تعديل. ضيف content-visibility: auto + contain-intrinsic-size على السكشنز اللي تحت الـ fold. شغّل Lighthouse تاني. لو ما لقيتش فرق ≥ 200ms على Slow 4G، المشكلة عندك مش في الـ rendering — على الأرجح في JavaScript blocking أو network waterfall، وده موضوع تاني.
المصادر
- W3C — CSS Containment Module Level 2:
https://www.w3.org/TR/css-contain-2/ - MDN — content-visibility:
https://developer.mozilla.org/en-US/docs/Web/CSS/content-visibility - web.dev — content-visibility: the new CSS property that boosts your rendering performance:
https://web.dev/articles/content-visibility - Can I use — content-visibility:
https://caniuse.com/css-content-visibility - WebKit Blog — Safari 18.0 Release Notes:
https://webkit.org/blog/15865/news-from-wwdc24-webkit-in-safari-18-beta/