用BEM和现代CSS选择器控制级联

本文为 360 奇舞团前端工程师翻译

原文标题:Taming the Cascade With BEM and Modern CSS Selectors

原文作者:Liam Johnston

原文地址:https://css-tricks.com/taming-the-cascade-with-bem-and-modern-css-selectors/

BEM。就像前端开发领域的所有技术一样,以BEM格式编写CSS可能是两极分化的。但它是——至少在我的 Twitter bubble 中——更受欢迎的 CSS 方法论之一。

就我个人而言,我认为 BEM 很好,我认为你应该使用它。但我也理解你为什么不这么做。

不管你对 BEM 的看法如何,它提供了几个好处,最大的好处是它有助于避免 CSS 级联中的特异性冲突。这是因为,如果使用得当,任何以 BEM 格式编写的选择器都应该有相同的特异性得分(0,1,0)。多年来,我为许多大型网站(比如政府、大学和银行)设计了 CSS,正是在这些大型项目中,我发现 BEM 真正发挥了作用。当你确信你正在编写或编辑的样式不会影响网站的其他部分时,编写CSS会更有趣。

实际上有一些例外情况,在这些情况下增加特异性被认为是完全可以接受的。例如::hover 和 :focus 伪类。它们的特异性得分为 0,2,0。另一种是伪元素,如 ::before 和 ::after,它们的特异性得分为 0,1,1。不过,对于本文的其余部分,让我们假设我们不希望出现任何其他的特异性变动。🤓

但我并不是来向你推荐 BEM 的。相反,我想谈谈我们如何将它与现代 CSS 选择器(比如 :is()、:has()、:where())一起使用,以获得对级联的更多控制。

什么是现代 CSS 选择器?

CSS Selectors Level 4规范为我们提供了一些强大的新方法来选择元素。我最喜欢的一些包括::is(),:where() 和 :not(),所有的现代浏览器都支持它们,并且现在几乎可以安全地用于任何项目。

:is() 和 :where() 基本上是一样的,除了它们如何影响特异性。具体来说,:where() 的特异性得分总是0,0,0。是的,甚至 :where(button#widget.some-class) 没有特异性。同时,:is() 的特异性是其参数列表中特异性最高的元素。因此,我们已经在可以使用的两个现代选择器之间进行了讨论区分。

功能强大得令人难以置信的 :has() 伪类也正在迅速获得浏览器支持(在我看来,这是自 Grid 以来CSS 最大的新特性)。然而,在撰写本文时,浏览器对 :has() 的支持还不足以用于生产。

让我在我的 BEM 中加入一个伪类,然后…

/* ❌ 特异性得分:0,2,0 */

.something:not(.something--special) {
  /* 给所有 somethings 添加样式, 除了special somethings */
}

哇!看到特异性评分了吗?请记住,对于BEM,我们理想情况下希望我们的选择器都具有 0,1,0 的特异性得分。为什么 0,2,0 不好?考虑一个类似的例子,如下:

.something:not(a) {
  color: red;
}
.something--special {
  color: blue;
}

尽管第二个选择器在源顺序中排在最后,但第一个选择器的更高特异性 (0,1,1) 胜出,.something--special 元素的颜色将被设置为红色。上述中,假设您的 BEM 编写正确,并且所选元素在 HTML 中应用了 .something 基类和 .something--special 修饰类。

如果使用不当 ,这些伪类会以意想不到的方式影响级联。正是这类不一致会导致令人头疼的问题,尤其是在更大更复杂的代码库上。

现在怎么办?

还记得我说过的 :where() 和它的特异性为零的事实吗?我们可以利用这一点:

/* ✅ 特异性得分: 0,1,0 */
.something:where(:not(.something--special)) {
  /* etc. */
}

这个选择器的第一部分(.something)的特异性得分为 0,1,0。但是 :where() 以及其中的所有内容的特异性都为0,这不会进一步增加选择器的特异性。

:where() 允许我们嵌套

像我一样不太关心特异性的人(公平地说,可能有很多人)在嵌套方面做得很好。通过一些随意的编码,我们可能会得到这样的CSS(注意,为了简洁起见,我使用Sass):

.card { ... }

.card--featured {
  /* etc. */  
  .card__title { ... }
  .card__title { ... }
}

.card__title { ... }
.card__img { ... }

在这个例子中,我们有一个 .card 组件。当它是“featured”卡片(使用 .card--featured 类)时,卡片的标题和图片需要采用不同的样式。但是,正如我们现在所知,上面的代码产生的特异性得分与我们系统的其他部分不一致。

一个比较古板的写法可能会这样做:

.card { ... }
.card--featured { ... }
.card__title { ... }
.card__title--featured { ... }
.card__img { ... }
.card__img--featured { ... }

还不错,对吧?坦率地说,这是漂亮的CSS。

不过,HTML也有一个缺点。经验丰富的 BEM 作者可能痛苦地意识到有条件地将修饰类应用于多个元素所需的笨重模板逻辑。在这个例子中,HTML模板需要有条件地将 --featured 修饰类添加到三个元素(.card、.card__title 和 .card__img)中,尽管在实际例子中可能更多。这将产生许多if语句。

:where() 选择器可以帮助我们在不增加特异性得分的情况下编写更少的模板逻辑,以及更少的 BEM 类。

.card { ... }
.card--featured { ... }

.card__title { ... }
:where(.card--featured) .card__title { ... }

.card__img { ... }
:where(.card--featured) .card__img { ... }

在 Sass 中情况差不多(注意后面的“&”):

.card { ... }
.card--featured { ... }
.card__title { 
  /* etc. */ 
  :where(.card--featured) & { ... }
}
.card__img { 
  /* etc. */ 
  :where(.card--featured) & { ... }
}

您是否应该选择这种方法,而不是将修饰类应用于各种子元素,这是个人偏好的问题。但至少 :where() 现在给了我们选择!

非BEM HTML呢?

我们不是生活在一个完美的世界里。有时您需要处理超出您控制范围的 HTML。例如,一个第三方脚本,它注入了您需要设置样式的 HTML。标记通常不是用 BEM 类名编写的。在某些情况下,这些样式根本不使用类,而是使用 ID!

再一次,我们能使用到 :where()。这个解决方案有点 hack,因为我们需要引用 DOM 树中更上层某个已知存在的元素的类。

/* ❌ 特异性得分: 1,0,0 */
#widget {
  /* etc. */
}

/* ✅ 特异性得分: 0,1,0 */
.page-wrapper :where(#widget) {
  /* etc. */
}

不过,引用父元素感觉有点风险和受限。如果父类发生了改变或者因为某种原因不存在了怎么办?更好的解决方案(但可能同样 hack)是使用 :is()。请记住,:is() 的特异性等于其选择器列表中特异性最高的选择器。

因此,我们可以引用一个虚构的类和 <body> 标签,而不是像上面的例子那样使用 :where() 并且引用我们知道(或希望!)存在的类。

/* ✅ 特异性得分: 0,1,0 */
:is(.dummy-class, body) :where(#widget) {
  /* etc. */
}

一直存在的 body 将帮助我们选择 #widget 元素,在同一个 :is() 中存在的 .dummy-class 类会为 body 选择器提供与类相同的特异性分数(0,1,0)…并且 :where() 的使用确保了选择器不会获得更多特异性。

就是这样!

这就是我们如何利用 :is() 和 :where() 伪类的现代特异性管理功能以及我们在以 BEM 格式编写 CSS 时获得的特异性冲突预防。在不久的将来,一旦 :has() 获得 Firefox 的支持(在撰写本文时,它目前在一个 flag 后面得到了支持),我们可能希望将其与 :where() 配对以撤销其特异性。

无论你是否全力支持 BEM 命名,我希望我们能达成共识,在选择器特异性方面保持一致是一件好事!

- END -

关于奇舞团

奇舞团是 360 集团最大的大前端团队,代表集团参与 W3C 和 ECMA 会员(TC39)工作。奇舞团非常重视人才培养,有工程师、讲师、翻译官、业务接口人、团队 Leader 等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。

4dde868524fdf57b5dc01be0b195ae48.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/7165.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

免费且好用的ssh工具FinalShell的下载与安装

一、FinalShell介绍 1.1 特色功能 云端同步,免费海外服务器远程桌面加速,ssh加速,本地化命令输入框,支持自动补全,命令历史,自定义命令参数。 1.2 主要特性 1.多平台支持Windows,macOS,Linux&#xff1b; 2.多标签,批量服务器管理&#xff1b; 3.支持登录ssh和Windows远程桌…

页面预加载优化实践

概述在客户端开发中&#xff0c;列表类型页面大多都依赖网络请求&#xff0c;需要等网络数据请求下来后再刷新页面。但遇到网络请求慢的场景&#xff0c;就会导致页面加载很慢甚至加载失败。我负责会员的商品列表页面&#xff0c;在业务场景中&#xff0c;页面元素比较复杂&…

【从零开始学习 UVM】10.2、UVM TLM —— UVM TLM Blocking Put Port

文章目录 UVM TLM Port Example1. 创建一个发送器类,其端口类型为 uvm_blocking_put_port2. 创建一个接收器类,实现 put 方法。3. 在更高层次上连接端口及其实现Put Port 阻塞行为任何组件都可以通过 TLM put port向另一个组件发送事务。接收组件应该定义 put port的实现。这…

KDWS-24便携式六氟化硫气体微量水份测定仪

一、技术特点 (1)自校准&#xff1a;传感器探头可自动校准零点&#xff0c;自动消除因零点、漂移而引入的系统误差&#xff0c;保证每次测量的准确性&#xff0c;同时可免去每年校验的繁琐。 (2)快速省气&#xff1a;开机进入测量状态后每SF6气隔露点测定时间为2min左右。 (3)…

【动手学习深度学习笔记】

第二章预备知识 节省内存&#xff1a;使⽤切⽚表⽰法将操作的结果分配给先前分配的数组 Z[:] XY就可以把结果覆盖在Z的原内存上&#xff0c;而不是新开辟内存&#xff0c;就节省了内存了 处理缺失值 inputs,outputs data.iloc[:,0:2],data.iloc[:,2] # iloc为位置索引 in…

rk3568 Android 添加IR遥控器

rk3568 添加IR遥控器 生活中充满了各种波长的电磁波&#xff0c;所谓的可见(色)光就是人眼可见的电磁波谱&#xff0c;其波长为 380~770nm&#xff0c;为了避免遥控器发射的光造成人眼不适及减少一般人造光源干扰&#xff0c;故选用人眼不可见的红外线(Infrared)波长&#xff…

Python 基础(六):基本数据类型

❤️ 博客主页&#xff1a;水滴技术 &#x1f338; 订阅专栏&#xff1a;Python 入门核心技术 &#x1f680; 支持水滴&#xff1a;点赞&#x1f44d; 收藏⭐ 留言&#x1f4ac; 文章目录一、字符串类型&#xff08;str&#xff09;1.1、引号嵌套1.2、转义字符1.3、改变大小写…

(论文加源码)基于deap数据集的transformer结合注意力机制脑电情绪识别

本篇论文是2021年新发表的一篇论文。也是目前有源码的论文中唯一一篇使用transformer模型和注意力机制的论文源码&#xff08;pytorch和tensorflow版本均有&#xff09; 论文及源码见个人主页&#xff1a; https://download.csdn.net/download/qq_45874683/87658878 &#xf…

ROS 2(二)熟悉ROS 2的使用和工具(turtlesim\ros2\rqt)

参考内容&#xff1a;http://docs.ros.org/en/humble/Tutorials/Beginner-CLI-Tools/Introducing-Turtlesim/Introducing-Turtlesim.html 1.简介 Turtlesim是一个用于学习ROS 2的轻量级模拟器。它说明了ROS 2在最基本的层面上所做的事情&#xff0c;便于了解以后如何处理真实的…

【面试】MyBatis面试题

文章目录MyBatis简介MyBatis是什么&#xff1f;Mybatis优缺点Hibernate 和 MyBatis 的区别ORM是什么为什么说Mybatis是半自动ORM映射工具&#xff1f;它与全自动的区别在哪里&#xff1f;传统JDBC开发存在什么问题&#xff1f;JDBC编程有哪些不足之处&#xff0c;MyBatis是如何…

【01 Capture Framework】

HAL3的Capture主要由HwNode中的P2CaptureNode以及其中的sub module来完成。 1. P2CaptureNode Overview P2CaptureNode也是继承自BaseNode以及IPipelineNode。其中组件主要有: 其中, P2CaptureNode:属于HwNode,重写了父类的init/config/queue/flush接口; P2CaptureProce…

蓝牙耳机品牌推荐:2023年降噪蓝牙耳机性价比推荐

每天上下班的地铁公交里&#xff0c;总会有很多嘈杂的声音发出&#xff0c;所以现在越来越多人选择佩戴一款降噪耳机来缓解消除一天的疲劳&#xff0c;在属于自己的空间里听听音乐。下面我推荐几款不错质量好的降噪耳机给大家&#xff0c;一起看看吧。 一、NANK南卡A2 价格&a…

Shell笔记--使用系统函数、自定义函数和Shell工具

​​​​​​​ 目录 1--basename和dirname系统函数 2--自定义函数 3--Shell常用工具 3-1--cut 3-2--sort 1--basename和dirname系统函数 ① basename 基本用法&#xff1a;basename [string / pathname] [suffix] basename&#xff1a;删除最后一个 / 字符&#xff08;…

接触过的第一台电脑-90年代的x86与如今的树莓派

#勤写标兵挑战赛#最早接触电脑是在幼儿园&#xff0c;那时候电脑下象棋都要输入命令行的。后来小学时候有了电脑课&#xff0c;要穿鞋套的。满满的回忆。那个时代电子产品更新很慢的&#xff0c;一台电脑可以用5年&#xff0c;286-386-486-586。486电脑的特点是&#xff1a;处理…

从零开始,三分钟内用Python快速自建一个私有化 ChatGpt 聊天机器人网站

用 Python 构建由 gpt-3.5-turbo API 支持的聊天机器人网站自2023年3月1日发布“ChatGPT API”以来&#xff0c;已经开发出了数千个基于该API的应用程序&#xff0c;为企业和个人开启了新的可能性时代。借助GPT-3.5的自然语言处理能力&#xff0c;用户可以创建能够无缝与人交互…

spring中产生bean的几种方式

BeanImportMyImportSelector implements ImportSelectorMyImportBeanDefinitionRegistarimplements ImportBeanDefinitionRegistrarFactoryBean这里着重讲解FactoryBean如何判断当前bean是否是FactoryBeanorg.springframework.beans.factory.support.AbstractBeanFactory#isFac…

linux安装tomcat(docker)

在终端输入&#xff0c;在docker hub上面查找tomcat镜像 docker search tomcat 从docker hub上拉取tomcat镜像到本地 docker pull tomcat 查看是否有拉取到的tomcat docker images 使用tomcat镜像创建容器实例&#xff08;也叫运行镜像&#xff09; docker run -it -p 8080…

学python的第十三天---小蓝(4)

贪心1、活动安排问题2、区间覆盖问题3、最优装载问题4、多机调度问题一、答疑&#xff08;贪心&#xff09;二、巧克力&#xff08;贪心&#xff09;三、顺子日期&#xff08;模拟&#xff09;四、特殊时间&#xff08;模拟&#xff09;五、乘积尾零&#xff08;模拟&#xff0…

简历问题总结

熟练掌握java相关知识&#xff0c;如IO流、集合框架、多线程等知识点。 ConcurrentHashMap中大量使用了CAS、多线程分步扩容&#xff0c;红黑树提高了并发情况下的访问速度。 put()操作先初始化Node[]数组table&#xff0c;默认容量是16。初始化Node[]数组前会使用Unsafe类的c…

【HTML系列】第五章 · 表单

写在前面 Hello大家好&#xff0c; 我是【麟-小白】&#xff0c;一位软件工程专业的学生&#xff0c;喜好计算机知识。希望大家能够一起学习进步呀&#xff01;本人是一名在读大学生&#xff0c;专业水平有限&#xff0c;如发现错误或不足之处&#xff0c;请多多指正&#xff0…