NVIDIA 初创加速计划,免费加速您的创业启动 了解详情
写点什么

CSS 选择器的一场革命,:has() 高级使用指南

作者 | Rahul Chhodde

  • 2023-07-10
    北京
  • 本文字数:4054 字

    阅读完需:约 13 分钟

CSS 选择器的一场革命,:has()高级使用指南

多年以来,CSS:has()伪类一直是最受期待的功能之一。这是一个 L4 级 CSS 选择器,目前已经在 Crhome 105 中受到完全支持,而且有望在短时间内快速普及到更多浏览器当中。

 

CSS 中的 :has() 是一个关系伪类,用于检查给定元素中是否包含某些子元素,如果符合匹配条件则将其选定,之后对样式做相应设置。

 

本文解释了 :has() 选择器的适用场景、一般用法、从简单到高级的各种用例、浏览器兼容性以及后备替换方案。

 

 :has() 选择器的现实意义

 

大多数开发人员习惯用 JavaScript 来实现那些 CSS 默认不支持的功能。然而,如今的网络浏览器已经发展得极其强大,也为各种新奇有趣的 CSS 功能打开了大门。

 

 :has() 选择器就是这样的功能之一。它适用于父元素而非子元素,使用逗号分隔的选择器列表作为参数,负责在它所表示的元素的各子元素间查找匹配项。其功能与 jQuery  :has() 方法颇为相似。

 

下面我们将一同回顾前端开发中 :has() 选择器的一些适用场景,了解开发者群体为何多年来一直期盼这项功能的实践落地。

 

首先自然是检查某个元素中是否包含其他某些元素,而后对其样式做相应设置。以往我们只能通过 JavaScript 实现这项操作,具体如下所示:

 

let content = document.querySelector("#content"),    headings = [] if(content) {  headings = content.querySelectorAll("h1, h2, h3")}if(headings.length) {  // do something}
复制代码

 

在以上代码中,我们可以使用 Web API 方法 querySelectorAll 来检查分区元素的标题。此方法将返回所提交元素的 NodeList。

 

如果 NodeList 为空,则意味着直接父级中不存在任何元素。JavaScript 版本的实现方式请参阅此处:

https://codepen.io/_rahul/pen/eYVQGwE

 

CSS:has()版本的实现方式请参阅此处:

https://blog.logrocket.com/advanced-guide-css-has-selector/#checking-multiple-children

 

其次是从子元素中选择父元素的能力。同样的,由于之前 CSS 不提供可直接操作的工具,所以开发人员习惯于用 Web API 的 parentNode 属性来实现:

 

let el = document.querySelector(".someElement"),    elParent = nullif(el) {  elParent = el.parentNode}
复制代码

 

上述代码的 CodePen 演示请参阅此处:

https://codepen.io/_rahul/pen/vYdQPMN

 

:has()选择器的实现请参阅此处

https://blog.logrocket.com/advanced-guide-css-has-selector/#selecting-parent

 

最后,:has() 选择器还能选择给定元素的上一个同级元素。CSS 中的同级选择器只允许选择下一个同级元素,但无法单凭 CSS 选择上一个同级元素。JavaScript 的实现如下所示(CodePen 完整演示:

https://codepen.io/_rahul/pen/oNEQKyz

 

let el = document.querySelector(".someElement"),    elPs = nullif(el) {  elPs = el.previousElementSibling}
复制代码

 

:has() 版本的完整代码请参阅此处:

 

https://blog.logrocket.com/advanced-guide-css-has-selector/#selecting-previous-sibling

 

可以看到,我们前面用 JavaScript 形式实现的所有功能,如今都能通过 CSS:has()搞定。下面,我们一同了解如何在谷歌 Chrome 上启用并测试这项功能。

 

启用 :has() 选择器

 

如果大家用的仍然是较旧的 Chrome 版本(v.101 至 104),则可通过 Chrome 标记启用此项功能。请确保您使用的是 Chrome 101 及以上版本,而后通过浏览器地址栏直接导航至 chrome://flags。

 

接下来将 Experimental Web Platform features 设置为 Enabled 即可。请重新启动浏览器,之后您就可以在 Chrome 浏览器中使用 CSS:has()了。



CSS :has() 选择器有啥作用?

 

现在咱们聊聊:has()伪类的用法和属性。作为一个伪类,它可以被附加至任何带有冒号的选择器上,并将接收到的类、ID 和 HTML 标签当作参数使用。

 

以下代码为:has()的常规用法和语法。仅当.selector 类包含使用:has()伪类以参数形式传递来的元素时,该类才会被选中:

 

.selector:has(.class) { ... }.selector:has(#id) { ... }.selector:has(div) { ... }
复制代码

 

可链接性

只要认为合适,大家完全可以一个接一个把多个:has()伪类链接起来。以下代码演示了此类链接的实现方式:

 

.selector:has(div):has(.class):has(#id) {  ...}
复制代码

 

通过参数列表实现多项选中

也可以提供一个包含多个元素的选择器列表,其形式与链接类似但效率更高:

 

.selector:has(div, .class, #id) {  ...}
复制代码

 

灵活性

 

假设大家意外向:has()伪类提交了无效的元素选择器,也请不必担心。:has()相当聪明,能够忽略掉无效项目并只处理有效选择器:

 

.selector:has(div, accordion, .class, ::lobster, #id) {  ...}
复制代码

 

accordion 和 ::lobster 属于无效选择器,这里会被:has()直接忽略。大家在开发者工具中不会看到任何 CSS 错误或警报。

 

使用场景

 

下面,我们来看 CSS:has()选择器在各种具体场景下的应用示例。

 

选中父元素

 

这可能是:has()选择器最常见的用法,因为其默认行为是选中包含一组特定元素的内容。但如果我们只知道其中的子元素,能否选中相应的父元素?





 通用选择器(*)可以跟:has()以及子组合器(>)配合使用,在无需了解相关信息的前提下快速选中父元素。

 

检查是否存在多个子元素

 

如前文属性讨论部分所述,:has()允许我们传递包含多个实体的列表,因此能够在给定元素中检查任意数量的选择条件。



选中上一个同级元素

 

通过将 CSS 相邻同级组合器同:has()伪类相结合,即可选中上一个同级元素。

 

有些朋友可能已经知道,相邻同级组合器能够选中给定元素的下一个同级元素。把这项操作跟:has()相结合,就能获取上一个同级元素。简单来讲,只要某个元素拥有下一个同级元素,那就可以用:has()加上+ 组合器的方式把这个元素选中!



有条件修饰

 

使用:has() 选择器,我们就不必单独对带有或不带有某些子元素的元素做单独设置。最典型的示例,自然就是带标题和不带标题的各种图形元素。



 假定有这样一个 figcaption 元素,其中不包含任何文本,我们该如何处理?对于这种情况,只要使用:not 和 :empty 选择器即可快速检查其内容。

 

与图形修饰类似,我们还可以在多个段落间切换各个引用块的文本对齐方式,具体请参阅此处的实际效果:

https://codepen.io/_rahul/pen/MWQZXoY

 

为空状态设置样式

 

之前我们已经拥有名为:empty 的伪类,用于对不包含任何内容的元素作样式设置。但它对空状态的处理存在问题,即使只包含一个空格,:empty 也会将该元素识别为非空。

 

这时候使用:empty 肯定效果不佳。假定我们要创建一个卡网格,其中包含一些无内容卡,这时就可以使用:has() 选择器来设计空卡状态的样式。



类型和块调整

 

在设计文章样式时,将类型和块元素保持对齐始终是项棘手的工作。下面,我们考虑同时涉及代码块、图形、块引用等通用类型元素的场景。

 

块元素间应该留有更多的垂直间距和修饰,以便在不同的字体条件下始终保持鲜明。这里可以用 CSS 功能来实现。

 

p {  margin: 0;}p:not(:last-child) {  margin-bottom: 1.5em;}h1,h2,h3,h4,h5,h6 {  line-height: 1.3;  margin: 1.5em 0 1.5rem;  color: #111;}pre,figure,blockquote {  margin: 3em 0;}figcaption {  margin-top: 1em;  font-size: 0.75em;  color: #888;  text-align: center;}
复制代码

 

以上代码片段会在标题和块元素之间生成不均匀的垂直间距,如下所示。要解决这个不均匀问题,我们可以使用之前提到的同级选中技巧试试看。

 


CTA 图标化


假定大家在 CSS 中创建了具有两种变体的 CTS(召唤按钮)或按钮组件:其一为默认的普通按钮,其二是带有图标的按钮。

 

以往我们恐怕需要为此编写两个单独的类,但现在有了:has()选择器,再也不必如此麻烦。我们只需要检查.btn 元素中的.btn-icon 子元素,然后将其设置成相应的样式即可。



布局调整


假设标题组件中有两种布局变体:其一是固定宽度,其二是动态宽度。

 

为了将标题内容保持在固定的宽度之内,我们必须在其中添加一个内容打包元素。这两个标头版本的标记间的唯一区别,就体现在打包元素上。

 

之后可以将 :has() 和 :not() 选择器配对使用并添加 CSS 类。



调整布局还有另一种用法,就是在网格中的列达到一定数量之后立即对其执行修改。

 

如果大家不想用 minmax() 函数来确定网格中各列的最适合宽度,那这种方法也很方便。

可以看到,只要标记超过两项,网格就会自动调整为三列。

 

改善表单可用性


只有将交互设计与反馈之间正确匹配起来,才能为交互设计找到最好的规划方向。而在设计交互式 HTML 表单时,向用户提供关于其输入的反馈显然是提升使用体验的大妙招。

 

在:has()、 :valid 和 :invalid 的帮助下,我们可以让表单更加动态,而且完全无需劳烦 JavaScript。



检查浏览器是否支持


如果浏览器不支持:has() 选择器,应该让以上示例触发错误弹窗以发出提醒。这种效果可以使用 @supports  CSS 规则实现,如以下代码所示:

 

@supports (selector(:has(*))) {  .selector:has(...) {    ...    }}
复制代码

 

大家可以这样一步步检查浏览器支持,并根据需要设置元素样式。如果某些代码库中直接用到某些新功能,则可通过以下方法为相同功能编写向下兼容的版本:

 

@supports not (selector(:has(*))) {  .selector {    ...    }}
复制代码

 

采取同样的方法,大家可以在浏览器不支持时向用户发出提醒。

 

还可以使用 JavaScript 中的 Support API 检测浏览器对不同功能的支持。以下为纯 JavaScript 版本的:has()支持检查示例:

 

if(!CSS.supports('selector(html:has(body))'))) { // if not supported  // Use the conventional JavaScript ways to emulate :has()}
复制代码

 

总结


在本文中,我们共同了解了 L4 级 CSS 选择器:has()。我们探讨了其现实意义,能够在前端项目中替代哪些 JavaScript 功能,以及从普通到高级的各种应用案例。

 

我们还涉及到不同浏览器的当前支持情况,以及如何检查您的浏览器是否支持这项新功能。

 

感谢耐心阅读,希望本文为您带来一点启发和帮助。欢迎大家在评论中分享更多关于:has()的实际应用案例。


相关阅读:


CSS 样式中颜色与颜色值的应用

避免使用 CSS @import 影响页面加载速度

css 过去及未来展望—分析 css 演进及排版布局的考量

这 10 个强大的 CSS 属性,每个前端都要懂

2023-07-10 17:503103

评论

发布
暂无评论
发现更多内容

面部表情识别在安全领域的应用

来自四九城儿

AI +悦数图数据库:图技术与大语言模型 LLM 结合,打造下一代知识图谱应用

最新动态

使用 RediSearch 在 Redis 中进行全文检索

AlwaysBeta

redis redisearch

What's the difference between Maxon WiFi6 4x4 M.2 industrial Wireless Module with QCN9074 & QCN9024?

wifi6module

QCN9074 QCN9024

面部表情识别在教育领域的应用

来自四九城儿

面部表情识别的伦理问题

来自四九城儿

技术分享 | 网页 frame 与多窗口处理

霍格沃兹测试开发学社

app测试中常用的Android模拟器

霍格沃兹测试开发学社

城市与AI,正待济沧海

脑极体

AI 大模型

HDC.Together 华为开发者大会还有干货,开发者主题演讲明日举行

Geek_2d6073

Rust中的高吞吐量流处理

高端章鱼哥

rust 流处理

技术分享 | web前端的HTML浅析

霍格沃兹测试开发学社

技术分享 | 白盒测试方法论

霍格沃兹测试开发学社

扎根x86市场稳步前进-海光投研报告解读

三掌柜

研究报告

appuploader不是开发者账号

【腾讯云Cloud Studio实战训练营】如何成为一名合格的Python爬虫“念咒师”(基于ChatGpt)

孤寒者

Python Cloud Studio Python爬虫 念咒师 念咒编程

appuploader不是开发者账号

雪奈椰子

华为负一屏全新体验,实时状态让服务快人一步

最新动态

探索大模型应用,解决企业数字化转型“最后一公里”

中关村科金

技术分享 | Selenium多浏览器处理

霍格沃兹测试开发学社

技术分享 | 黑盒测试方法论-判定表

霍格沃兹测试开发学社

盘点国产CPU“六君子”之中科海光发展史

石云升

cpu 微处理器 中科海光

从海光看国内芯片行业崛起:技术研发是增长关键

打工人!

芯片 芯片行业 海光

组织门户支持成员自主公开,快速搭建内容|ModelWhale 版本更新

ModelWhale

云计算 数据分析 API 算力 数据门户

准确率提升近4%,自研ASR模型助力公安机关筑牢反诈安全网

中关村科金

开源社区寻找八月创作之星!你准备好了吗~

Openlab_cosmoplat

人工智能 开源

海光财报解读:深耕研发,打造技术护城河

程序员海军

研究报告 海光

详解 HashMap 的底层实现原理

树上有只程序猿

Java 数据结构 hashmap 哈希

私有化部署企业IM即时通讯:提升效率、防止泄密、高效协同办公

WorkPlus

局域网部署,用WorkPlus视频会议保密又安全

WorkPlus

华为正式发布HarmonyOS NEXT开发者预览版,携手开发者共赴鸿蒙生态星辰大海

Geek_2d6073

CSS 选择器的一场革命,:has()高级使用指南_编程语言_InfoQ精选文章