التصميم المستند إلى المحتوى

ميزات الويب: :has()‎ :not()‎

في السابق، كان تطبيق تنسيقات مختلفة على مكوّن استنادًا إلى محتواه يتطلّب إما JavaScript أو منطقًا شرطيًا في لغة نموذج HTML لإدخال فئات التعديل (مثل .card--has-image أو .card--text-only).

تغني الفئة الزائفة :has() عن ذلك من خلال العمل كمحدِّد رئيسي. تتيح لك هذه السمة تصميم عنصر حاوية بشكل مشروط استنادًا إلى وجود عناصر فرعية معيّنة أو عدم وجودها.

باستخدام :has()، يمكنك بسهولة تحديد صيغ مختلفة للتنسيق بالكامل في CSS استنادًا إلى محتوى DOM الفعلي للمكوّن. يمكنك أيضًا دمجها اختياريًا مع :not() لاستهداف غياب المحتوى بشكل صريح لتحديد التنسيقات التلقائية.

تنفيذ نمط الحاوية المستند إلى المحتوى

إلزامي: يجب استخدام أداة الاختيار :has() في عنصر الحاوية لرصد توفّر محتوى فرعي معيّن.

لإنشاء مكوّن يغيّر تنسيقه استنادًا إلى محتواه:

  1. تحديد النمط التلقائي: طبِّق أنماط التنسيق الأساسية على عنصر الحاوية (على سبيل المثال، حزمة بسيطة من عمود واحد).
  2. تطبيق عمليات إلغاء مستندة إلى المحتوى: استهدِف الحاوية باستخدام :has([child-selector]) وطبِّق أنماط التنسيق الجديدة عند توفّر هذا المحتوى (على سبيل المثال، شبكة متعددة الأعمدة).

مثال: مكوّن بطاقة يتحوّل إلى تنسيق جنبًا إلى جنب في حال توفُّر صورة

/* 1. Define the default state on the component container */
/* This applies when there is NO image */
.article-card {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding: 1.5rem;
  border: 1px solid #ccc;
  border-radius: 8px;
}

/* 2. Apply styles when a specific child element is present */
/* MANDATORY: Target the container and use :has() to check for the descendant */
.article-card:has(img) {
  /* Change the layout to a row if an image exists */
  flex-direction: row;
  align-items: center;
}

/* Optional: Style the image itself (no :has() needed here) */
.article-card img {
  width: 150px;
  height: auto;
  border-radius: 4px;
}

/* You can also combine :has() and :not() to explicitly target the ABSENCE of content */
/* This selects a card that DOES NOT have an image */
.article-card:not(:has(img)) {
  /* E.g., apply a different background color for text-only cards */
  background-color: #f9f9f9;
}
<!-- Assume an <h1> precedes these components in the document layout -->
<!-- A card WITH an image (will use row layout) -->
<article class="article-card">
  <img src="thumbnail.jpg" alt="Article thumbnail" />
  <div class="content">
    <h2>Card With Image</h2>
    <p>This card lays out its content horizontally.</p>
  </div>
</article>

<!-- A card WITHOUT an image (will use default column layout) -->
<article class="article-card">
  <div class="content">
    <h2>Text-Only Card</h2>
    <p>This card lays out its content vertically, and gets its background color from the :not(:has()) rule.</p>
  </div>
</article>

نصيحة بشأن الأداء: عند استخدام :has()، حدِّد نطاق أداة الاختيار على حاوية المكوّنات الأكثر تحديدًا (مثل .article-card). تجنَّب ربطها بعناصر عالية المستوى مثل body:has(img) إذا كانت تغييرات التصميم محلية للغاية، لأنّ طلبات البحث الواسعة النطاق في :has() يمكن أن تؤدي إلى إعادة احتساب التنسيق بشكل أكبر.

استراتيجيات احتياطية

إذا كان نمط التنسيق المستند إلى المحتوى مهمًا لتجربة المستخدم أو تصميم الصفحة، يجب توفير خيار احتياطي للمتصفّحات التي لا تتيح استخدام أداة الاختيار :has(). بالنسبة إلى التأثيرات الزخرفية البحتة، يمكن استخدام :has() كتحسين تدريجي بدون عنصر احتياطي.

إلزامي: عند تنفيذ إجراء بديل للتنسيقات المهمة، يجب استخدام @supports not selector(:has(*)) في CSS لتحديد إجراء بديل تقليدي يستند إلى الفئات (مثل .has-image).

على عكس الأنماط التفاعلية المستندة إلى الحالة، عادةً ما يكون حضور المحتوى معروفًا في وقت العرض. أفضل حلّ بديل هو أن يضيف محرّك النماذج من جهة الخادم (أو منشئ المواقع الإلكترونية الثابتة) فئة مثل .has-image إلى الحاوية إذا كان العنصر الفرعي (مثل صورة) متوفّرًا في البيانات.

إذا لم يكن العرض من جهة الخادم خيارًا متاحًا، عليك استخدام نص برمجي صغير مع CSS.supports() لرصد المحتوى وإلحاق الفئة عند التحميل أو بعد إدخال المحتوى الديناميكي.

/* Fallback CSS for older browsers */
/* We check if the browser DOES NOT support the :has() selector */
@supports not selector(:has(*)) {
  /* Define a traditional modifier class that applies the exact same layout overrides */
  .article-card.has-image {
    flex-direction: row;
    align-items: center;
  }
  
  .article-card:not(.has-image) {
    background-color: #f9f9f9;
  }
}
/* Fallback JavaScript for older browsers (if not using SSR to add the class) */
/* Check for support before running the script to avoid unnecessary work in modern browsers */
if (!CSS.supports('selector(:has(*))')) {
  // Find all components that need checking
  const cards = document.querySelectorAll('.article-card');
  
  cards.forEach(card => {
    // If the critical content exists, manually add the fallback class
    if (card.querySelector('img')) {
      card.classList.add('has-image');
    }
  });
}