سبک‌بندی مبتنی بر محتوا

ویژگی‌های وب: :has()‎ :not()‎

در گذشته، اعمال چیدمان‌های مختلف به یک مؤلفه براساس محتوای آن، به جاوا اسکریپت یا منطق شرطی در زبان قالب‌بندی 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');
    }
  });
}