依內容設定樣式

網頁功能: :has() :not()

過去,如要根據元件內容套用不同版面配置,必須在 HTML 範本語言中使用 JavaScript 或條件邏輯,插入修飾符類別 (例如 .card--has-image 或 .card--text-only)。

:has() 虛擬類別可做為父項選取器,因此不需要這麼做。您可以根據特定子系元素是否存在,有條件地設定容器元素的樣式。

使用 :has(),您就能根據元件的實際 DOM 內容,輕鬆在 CSS 中定義不同的版面配置變化版本。您也可以視需要搭配 :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() 可做為漸進式增強功能,不需備用。

必要:為重要版面配置導入備用方案時,必須在 CSS 中使用 @supports not selector(:has(*)) 定義傳統的類別型備用方案 (例如 .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');
    }
  });
}