产品战略专家梁宁确认出席AICon北京站,分享AI时代下的商业逻辑与产品需求 了解详情
写点什么

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

评论

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

“东数西算”超级工程利好云计算,多云管理背后却暗藏汹涌!

行云管家

云计算 多云 东数西算 云管

Spring Cloud入门教程-使用Hystrix Dashboard 监控熔断器的状态

爱好编程进阶

Java 面试 后端开发

SpringBoot系列:RabbitMq讲解与示例

爱好编程进阶

Java 面试 后端开发

《零基础》MySQL 连接(四)

爱好编程进阶

Java 面试 后端开发

thinkphp5框架新建页面相关规范详解

CRMEB

2022年提高远程工作效率的三大实用技巧汇总

行云管家

远程办公 居家办公 办公软件

【源码分析设计模式 10】SpringMVC中的建造者模式

爱好编程进阶

Java 面试 后端开发

Tiger DAO VC:将你的风险投资变成DAO组织协同

小哈区块

macOS系统病毒常见的两种传播途径

火绒安全

macos 终端安全 勒索病毒 蠕虫

TASKCTL调度服务(主/从)代理节点的启动和停止

敏捷调度TASKCTL

数据仓库 kettle 调度引擎 ETL 调度任务

SpringBoot+WebSocket实时监控异常

爱好编程进阶

Java 面试 后端开发

计算机存储层次简析

懒时小窝

计算机基础

【并发编程系列10】阻塞队列之SynchronousQueue

爱好编程进阶

Java 面试 后端开发

【并发编程系列1】Thread生命周期及interrupted()作用分析

爱好编程进阶

Java 面试 后端开发

STM32+华为云IoTDA,带你设计一个属于自己的动态密码锁

华为云开发者联盟

stm32 iotda 华为云IoT 密码锁 Qt框架

“双碳”下的建筑业:未来10年必须重视这5大方向

WorkPlus

Spring(十)

爱好编程进阶

面试 后端开发Java

云图说丨叮咚,您有一份短信通关攻略待查收

华为云开发者联盟

短信 签名 消息 签名模板 MSGSMS

YonMaster开发者认证线上赋能培训班定档4月18日

YonBuilder低代码开发平台

java高级用法之:JNA中的Function

程序那些事

Java Netty 程序那些事 4月月更

WeTest平台产品&技术合作伙伴招募

WeTest

阿里云视频云人脸生成领域最新研究成果入选CVPR2022

阿里云视频云

计算机视觉 视频编码 CVPR 视频云

Tiger DAO VC:DAO组织风险投资时代来临

西柚子

jackson学习之十(终篇):springboot整合(配置类)

程序员欣宸

Java web 4月月更

RadonDB MySQL on K8s 2.1.4 发布!

RadonDB

MySQL 数据库 Kubernetes 高可用 RadonDB

简单实用的redis分布式锁

Rubble

4月日更 4月月更

来也科技收购Mindsay背后:新旧势力交锋智能自动化备受关注

王吉伟频道

RPA 收购 机器人流程自动化 来也科技 Mindsay

聚焦供应链布局,新能源汽车企业重塑产业核心竞争能力

数商云

数字化转型 供应链 新能源汽车

SSM 最简单最全面的整合Spring+SpringMVC+Mybatis三大框架 快速搭建

爱好编程进阶

Java 面试 后端开发

Vue 学习笔记(3)路由的基本使用 结合 SpringBoot

爱好编程进阶

Java 面试

小程序能当成 App 吗?

FinClip

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