:has() 的更多现实世界用例

Avatar of Liam Johnston
Liam Johnston

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

:has() 伪类 绝对是我最喜欢的 CSS 新特性。我知道对于你们中的许多人来说也是如此,至少 参加了 CSS 状态调查的你们 是这样认为的。反向编写选择器的能力赋予了我们更多我从未想过可能实现的超能力。

我说“更多超能力”,是因为已经有很多非常棒的巧妙想法被一群超级聪明的人发布,例如

本文并非 :has() 的权威指南。它也不是为了重复已经说过的内容。我只是(嗨 👋)短暂地加入潮流,分享一些我日常工作中最有可能使用 :has() 的方法…… 也就是在浏览器支持足够好的情况下。(Firefox 是最后一个坚持不懈的浏览器,但会支持它,即将到来。)

当这种情况发生时,您可以肯定我会在所有地方开始使用 :has()。以下是我最近构建的一些现实世界示例,我当时想,“一旦 :has() 得到完全支持,这将变得非常棒。”

避免需要访问 JavaScript 组件外部

您是否曾经构建过一个交互式组件,该组件有时需要影响页面其他地方的样式?以以下示例为例,其中 <nav> 是一个 超级菜单,打开它会更改其上方 <header> 内容的颜色。

我觉得我需要一直做这样的事情。

此特定示例是我为某个站点制作的 React 组件。我不得不使用 document.querySelector(...) “访问” React 页面的一部分,并在 <body><header> 或其他组件上切换一个类。这并不是世界末日,但确实感觉有点糟糕。即使在完全的 React 站点(例如 Next.js 站点)中,我也必须在管理更高级别组件树上的 menuIsOpen 状态或执行相同的 DOM 元素选择之间做出选择——这不太 React 风格。

使用 :has(),问题消失了

header:has(.megamenu--open) {
  /* style the header differently if it contains 
    an element with the class ".megamenu--open"
  */
}

不再需要在我的 JavaScript 组件中处理 DOM 的其他部分!

更好的表格条纹 UX

在表格中添加交替的行“条纹”可以改善 UX。它们可以帮助您的眼睛在扫描表格时跟踪您所在的行。

但根据我的经验,这在只有两三行的表格上效果不佳。例如,如果您有一个在 <tbody> 中有三行的表格,并且您正在对每个“偶数”行进行“条纹”处理,则最终可能只有一个条纹。这实际上不值得使用模式,并且可能会让用户想知道为什么那一个突出显示的行如此特殊。

使用 Bramus 使用 :has() 根据子元素数量应用样式 的这种技术,我们可以在超过(例如)三行时应用表格条纹

想要更花哨?您还可以决定仅在表格至少具有一定数量的列时执行此操作

table:has(:is(td, th):nth-child(3)) {
  /* only do stuff if there are three or more columns */
}

从模板中删除条件类逻辑

我经常需要根据页面上的内容更改页面布局。以以下网格布局为例,其中主要内容的放置位置会根据是否存在侧边栏更改网格区域。

Layout with left sidebar above a layout with no sidebar.

这可能取决于 CMS 中是否设置了同级页面。我通常会使用模板逻辑在布局包装器中条件添加 BEM 修饰符类 来处理这两种布局。该 CSS 可能如下所示(为简洁起见省略了响应式规则和其他内容)

/* m = main content */
/* s = sidebar */
.standard-page--with-sidebar {
  grid-template-areas: 's s s m m m m m m m m m';
}
.standard-page--without-sidebar {
  grid-template-areas: '. m m m m m m m m m . .';
}

当然,从 CSS 角度来看,这完全没问题。但这确实使模板代码有点混乱。根据您的模板语言,条件添加一堆类可能会变得非常丑陋,尤其是在您必须对大量子元素执行此操作时。

将其与基于 :has() 的方法进行对比

/* m = main content */
/* s = sidebar */
.standard-page:has(.sidebar) {
  grid-template-areas: 's s s m m m m m m m m m';
}
.standard-page:not(:has(.sidebar)) {
  grid-template-areas: '. m m m m m m m m m . .';
}

老实说,从 CSS 角度来看,这并没有好多少。但如果您问我,从 HTML 模板中删除条件修饰符类是一个不错的优势。

很容易想到 :has() 的微型设计决策——例如,当卡片中包含图像时——但我认为它对于这些宏观布局更改也非常有用。

更好的特异性管理

如果您阅读了 我之前的文章,您就会知道我是一个特异性狂热者。如果您像我一样,不希望在整个样式中添加 :has():not() 时特异性分数激增,请务必使用 :where()

这是因为 :has() 的特异性基于其参数列表中最特定的元素。因此,如果您在其中使用了 ID 之类的东西(我不知道为什么!),您的选择器在级联中将难以覆盖。

另一方面,:where() 的特异性始终为零,从不增加特异性分数。

/* specificity score: 0,1,0.
  Same as a .standard-page--with-sidebar 
  modifier class
*/
.standard-page:where(:has(.sidebar)) {
  /* etc */
}

未来一片光明

这些只是我迫不及待想要在生产环境中使用的一些东西。CSS-Tricks 年鉴中也有一些示例。您期待使用 :has() 做些什么?您在哪些现实世界示例中遇到过 :has() 将是完美解决方案的情况?