写点什么

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:503663

评论

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

荣耀主题设计师邀请有礼

荣耀开发者服务平台

设计师 资源分享 安卓 ios 活动推荐

USDT质押挖矿(智能合约)系统开发(成熟技术)

V\TG【ch3nguang】

LP丨IDO丨DeFi流动性质押挖矿系统制度开发设计

V\TG【ch3nguang】

扫盲低代码——基本原理

树上有只程序猿

软件开发 低代码 JNPF

虚拟币比特币质押挖矿|算力挖矿模式系统搭建开发

V\TG【ch3nguang】

外贸独立站谷歌SEO优化:提升网站排名和流量的关键要素

九凌网络

OP 链 DAPP 智能合约质押挖矿系统开发(Python技术搭建)

V\TG【ch3nguang】

关于亚马逊 CodeWhisperer 的使用体验

亚马逊云科技 (Amazon Web Services)

人工智能 云上探索实验室 Amazon CodeWhisperer

Typora for Mac(Markdown文本编辑器) 1.7.6中文直装版

mac

Typora markdown编辑器 苹果mac Windows软件

Nautilus Chain 联合香港数码港举办 BIG DEMO DAY活动,释放何信号?

大瞿科技

文心一言 VS 讯飞星火 VS chatgpt (122)-- 算法导论10.4 3题

福大大架构师每日一题

福大大架构师每日一题

云质押挖矿项目系统开发DeFi模式/云算力挖矿系统开发技术讲解

V\TG【ch3nguang】

提高数据研究效率,优化成果分享及复用|ModelWhale 版本更新

ModelWhale

人工智能 低代码 数据服务 模型服务 数据引用

OmniFocus Pro 3 for Mac(GTD时间管理工具)v3.15.4正式版

mac

苹果mac Windows软件 OmniFocus GTD时间管理工具

第14期 | GPTSecurity周报

云起无垠

“草料二维码”插件登陆飞书多维表格,可批量生成二维码标签

草料二维码

二维码 飞书

权威认可!应急专家吴志强少将高度认可卓翼系留无人机高层灭火解决方案

硬科技星球

外贸网站如何做好站内优化

九凌网络

Spring高手之路15——掌握Spring事件监听器的内部逻辑与实现

砖业洋__

spring Spring 监听 ApplicationEventMulti Spring事件 PayloadApplicationEvent

DAPP去中心化流动性LP质押挖矿系统开发

V\TG【ch3nguang】

深度剖析低代码平台

互联网工科生

低代码开发平台 JNPF

PRT普瑞缇ProTradex系统技术开发丨质押挖矿开发详情

V\TG【ch3nguang】

iOS安全加固方法及实现

雪奈椰子

一图读懂融云出海 & 全球化通信方案

融云 RongCloud

全球化 IM RTC 图论 出海

Swagger API 未授权访问漏洞问题解决方法

Liam

Java 程序员 API swagger Swagger API

MobPush厂商通道回执配置指南(Vivo,荣耀)

MobTech袤博科技

API商品数据接口调用爬虫实战

Noah

如何应用 NFTScan NFT API 在 Aptos 网络上开发 Web3 应用

NFT Research

NFT\ NFTScan nft工具

MobPush数智化推送,精准定位万圣节狂欢年轻一族

MobTech袤博科技

外贸网站流量下降可能是这五点原因造成的

九凌网络

网络拓扑图一本通:手把手教你怎么画

职场工具箱

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