过去,若要根据组件的内容为其应用不同的布局,您需要在 HTML 模板语言中使用 JavaScript 或条件逻辑来注入修饰符类(例如 .card--has-image 或 .card--text-only)。
:has() 伪类可充当父级选择器,从而消除这种需求。它允许您根据特定后代元素的存在与否,有条件地设置容器元素的样式。
使用 :has(),您可以完全在 CSS 中根据组件的实际 DOM 内容轻松定义不同的布局变体。您还可以选择将其与 :not() 结合使用,以明确针对缺少内容的情况来定义默认布局。
实现基于内容的容器样式
强制性:您必须在容器元素上使用 :has() 选择器,以检测是否存在特定的子内容。
如需构建一个根据其内容更改布局的组件,请执行以下操作:
- 定义默认样式:将基本布局样式应用于容器元素(例如,简单的单列堆叠)。
- 应用基于内容的替换:使用
: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');
}
});
}