CSS :has() 選擇器實戰:父選擇器改變元件樣式架構

前端工程(更新於 2026年6月9日)

:has() 選擇器:CSS 的「父選擇器」

長期以來,CSS 只能從父到子選擇元素。:has() 打破了這個限制,實現了從子到父的反向選擇:

/* 傳統:只能選子元素 */
.card .title { color: blue; }

/* :has():根據子元素狀態選父元素 */
.card:has(.title) { border-color: blue; }

瀏覽器支援

瀏覽器 支援版本 發布時間
Chrome 105+ 2022.08
Safari 15.4+ 2022.03
Firefox 121+ 2023.12
Edge 105+ 2022.08

截至 2026 年,所有主流瀏覽器均已支援 :has()


語法與匹配邏輯

基本語法

/* 包含指定後代 */
parent:has(descendant) { }

/* 包含直接子元素 */
parent:has(> child) { }

/* 兄弟元素存在 */
element:has(+ sibling) { }
element:has(~ sibling) { }

/* 多條件組合 */
element:has(.a, .b) { }    /* 或 */
element:has(.a):has(.b) { } /* 且 */

與 :not() 組合

/* 不包含 .active 子元素的 .nav */
.nav:not(:has(.active)) {
  opacity: 0.5;
}

/* 包含未選中的 checkbox */
.form:has(input:not(:checked)) {
  border-color: orange;
}

實戰案例 1:表單驗證狀態

傳統 JS 方案

// 需要監聽每個輸入框的驗證狀態
input.addEventListener('invalid', () => {
  form.classList.add('has-error');
});
input.addEventListener('valid', () => {
  form.classList.remove('has-error');
});

:has() 方案

/* 表單包含無效輸入時,整體樣式變化 */
form:has(:user-invalid) {
  border-color: #ef4444;
}

form:has(:user-invalid) .submit-btn {
  opacity: 0.5;
  pointer-events: none;
}

/* 單個欄位無效時,其容器高亮 */
.field:has(:user-invalid) {
  background: #fef2f2;
}

.field:has(:user-invalid) .error-msg {
  display: block;
}

/* 全部有效時顯示成功提示 */
form:has(.field:not(:has(:user-invalid))) .success-msg {
  display: block;
}
<form>
  <div class="field">
    <label>電子郵件</label>
    <input type="email" required />
    <span class="error-msg">請輸入有效電子郵件</span>
  </div>
  <div class="field">
    <label>密碼</label>
    <input type="password" required minlength="8" />
    <span class="error-msg">密碼至少 8 位</span>
  </div>
  <button class="submit-btn">提交</button>
  <span class="success-msg">表單有效</span>
</form>

實戰案例 2:卡片版面配置

含圖片與無圖片的卡片自適應

/* 有圖片的卡片:水平版面配置 */
.card:has(img) {
  display: flex;
  gap: 1rem;
}

.card:has(img) .card-body {
  flex: 1;
}

/* 無圖片的卡片:垂直版面配置 */
.card:not(:has(img)) {
  display: block;
  text-align: center;
}

/* 圖片為橫圖時調整比例 */
.card:has(img[width="1200"]) {
  flex-direction: column;
}

卡片懸停效果

/* 滑鼠懸停在卡片任意位置,圖片縮放 */
.card:has(.card-link:hover) img {
  transform: scale(1.05);
}

.card:has(.card-link:hover) .card-title {
  color: var(--primary);
}

實戰案例 3:導覽高亮

傳統方案:伺服器端渲染時加入 active class

<nav>
  <a href="/" class="active">首頁</a>
  <a href="/blog">部落格</a>
</nav>

:has() 方案:純 CSS 感知當前頁面

/* 利用 aria-current 屬性 */
nav:has([aria-current="page"]) {
  border-bottom-color: var(--primary);
}

nav a:has(+ [aria-current="page"]),
nav a[aria-current="page"] {
  color: var(--primary);
  font-weight: 600;
}

/* 導覽中有啟用項時,顯示回上一頁按鈕 */
nav:not(:has([aria-current="page"])) .back-btn {
  display: none;
}

側邊欄折疊感知

/* 側邊欄有展開的子選單時,顯示分隔線 */
.sidebar:has(.submenu:not([hidden])) {
  border-right: 2px solid var(--border);
}

/* 所有子選單都折疊時,緊湊模式 */
.sidebar:not(:has(.submenu:not([hidden]))) {
  width: 64px;
}

實戰案例 4:暗色模式聯動

根據圖片亮度自動調整

/* 當頁面包含高亮度圖片時,周圍文字加深 */
figure:has(img.light) {
  background: #1a1a1a;
  color: #fff;
}

/* 暗色模式下,包含透明圖片的容器加背景 */
@media (prefers-color-scheme: dark) {
  .container:has(img[alt*="logo"]) {
    background: #fff;
    padding: 8px;
    border-radius: 8px;
  }
}

實戰案例 5:表格與列表互動

表格全選感知

/* 所有 checkbox 都選中時,表頭樣式變化 */
table:has(tbody input:checked:not(:indeterminate):not(:only-child))
  th {
  background: var(--primary-light);
}

/* 有部分選中時,顯示批次操作欄 */
table:has(tbody input:checked) .batch-actions {
  display: flex;
}

/* 無選中時隱藏 */
table:not(:has(tbody input:checked)) .batch-actions {
  display: none;
}

列表空狀態

/* 列表為空時顯示空狀態提示 */
.list:has(> :only-child.empty-placeholder) {
  justify-content: center;
  min-height: 200px;
}

:has() 與 JavaScript 的取捨

場景 :has() 優勢 JS 優勢
DOM 結構變化 自動回應,無需監聽 可處理非同步資料
效能 瀏覽器原生,樣式計算快 可精細控制計算時機
複雜邏輯 簡單條件判斷 多步計算、API 呼叫
相容性 2023 年後全支援 無相容問題

效能注意事項

/* 差:深層嵌套的 :has() 可能影響效能 */
.container:has(.deep1:has(.deep2:has(.deep3))) { }

/* 好:扁平的 :has() 條件 */
.container:has(.deep3) { }

避免多層嵌套 :has(),瀏覽器需要遍歷 DOM 樹來匹配,嵌套越深開銷越大。


與其他 CSS4 特性的配合

:is() 和 :where() 簡化選擇器

/* 使用 :is() 簡化多型別判斷 */
.card:has(:is(img, video, svg)) {
  aspect-ratio: 16/9;
}

/* :where() 降低優先級,方便覆蓋 */
.card:where(:has(.featured)) {
  border-color: gold;
}
/* 頁面有開啟的強制回應對話方塊時,禁止背景捲動 */
body:has(:modal) {
  overflow: hidden;
}

body:has([popover]:not(:popover-closed)) {
  overflow: hidden;
}

最佳實務總結

  • 優先用 :has() 替代 JS class 切換:減少 JS 程式碼,樣式邏輯內聚
  • 保持 :has() 條件扁平:避免多層嵌套影響效能
  • 與語意屬性配合aria-current:user-invalid:checked
  • 漸進增強:核心功能不依賴 :has(),:has() 作為增強
  • 配合 DevTools 除錯:Chrome DevTools 已支援 :has() 選擇器高亮

總結

:has() 選擇器是 CSS 發展史上最重要的能力擴展之一,它讓 CSS 具備了從子到父的反向選擇能力,大幅減少了表單驗證、條件樣式、狀態聯動等場景的 JavaScript 程式碼。掌握 :has() 的匹配邏輯和效能邊界,是現代前端工程師的必備技能。

使用 Flexbox 工具 練習版面配置組合,使用 Box Shadow 產生器 建立卡片陰影效果,使用 Border Radius 工具 設計卡片圓角。

本站提供瀏覽器本地工具,免註冊即可試用 →

#:has()#CSS选择器#父选择器#条件样式#CSS4