写点什么

CSS is、where 和 has 选择器

作者:南城FE
  • 2023-07-10
    广东
  • 本文字数:3271 字

    阅读完需:约 11 分钟

CSS is、where和has选择器

本文翻译自 How the CSS :is, :where and :has Pseudo-class Selectors Work,作者:Craig Buckler 略有删改


CSS 选择器允许我们按 HTML 文档中的类型、属性或位置选择元素。本教程介绍了三个新选项:is():where():has()


选择器通常用于样式表中。以下示例选中所有<p>段落元素并将字体粗细更改为粗体:


p {  font-weight: bold;}
复制代码


你也可以在新版的 JavaScript 中使用选择器来定位 DOM 节点:


  • document.querySelector() 返回第一个匹配的 HTML 元素

  • document.querySelectorAll() 返回所有匹配的 HTML 元素 NodeList 节点列表


伪类选择器基于 HTML 元素的当前状态来定位它们。用到比较多的是:hover,它在光标移动到元素上时应用样式,因此它用于突出显示可单击的链接和按钮。还有如下伪类选择器供我们使用:


  • :visited:匹配访问过的链接

  • :target:匹配文档有 URL 指向的元素

  • :first-child:选中目标元素的第一个子元素

  • :nth-child:选择特定的子元素

  • :empty:匹配一个没有内容或子元素的元素

  • :checked:匹配已打开的复选框或单选按钮

  • :blank:设置未输入字段的情况

  • :enabled:匹配已启用的输入字段时

  • :disabled:匹配禁用的输入字段时

  • :required:针对必填输入字段时

  • :valid:匹配有效的输入字段时

  • :invalid:匹配无效的输入字段时

  • :playing:针对播放的音频或视频元素


浏览器最近又增加了三个伪类选择器。

:is 伪类选择器

通常需要将相同的样式应用于多个元素。例如,<p>段落文本默认为黑色,但当它出现在<article><section><aside>中时,则为灰色:


/* default black */p {  color: #000;}
/* gray in <article>, <section>, or <aside> */article p,section p,aside p { color: #444;}
复制代码


这是一个简单的示例,但是更复杂的页面将导致更复杂和冗长的选择器字符串。任何选择器中的语法错误都可能破坏所有元素的样式。


我们也可以使用 CSS 预处理器,如 Sass,允许嵌套使用:


article, section, aside {  p {    color: #444;  }}
复制代码


最终也将创建相同的 CSS 代码,减少开发工作量,并可以防止错误。但是:


  • 需要一个 CSS 构建工具。比如 Sass 这样的预处理器,但这可能会给某些开发团队带来复杂性。

  • 嵌套可能会导致其他问题。构建深度嵌套的选择器很容易,而这些选择器越来越难以读取和输出冗长的 CSS。


:is()提供了一个原生的 CSS 解决方案,在所有现代浏览器(不包括 IE)中都有完全支持:


:is(article, section, aside) p {  color: #444;}
复制代码


单个选择器可以包含任意数量的:is()伪类。例如,下面的复杂选择器将绿色文本颜色应用于所有<h1><h2><p>元素,这些元素是具有<section>.primary类的.secondary的子元素,并且不是<article>的第一个子元素:


article section:not(:first-child):is(.primary, .secondary) :is(h1, h2, p) {  color: green;}
复制代码


没有:is()的等价代码需要六个 CSS 选择器:


article section.primary:not(:first-child) h1,article section.primary:not(:first-child) h2,article section.primary:not(:first-child) p,article section.secondary:not(:first-child) h1,article section.secondary:not(:first-child) h2,article section.secondary:not(:first-child) p {  color: green;}
复制代码


:is()不能匹配::before::after伪元素,因此以下示例代码将失效:


div:is(::before, ::after) {  display: block;  content: '';  width: 1em;  height: 1em;  color: blue;}
复制代码

:where 伪类选择器

:where()选择器语法与:is()相同,并且在所有现代浏览器(不包括 IE)中都支持。这通常会导致相同的样式。例如:


:where(article, section, aside) p {  color: #444;}
复制代码


区别在于特异性。特异性是用于确定哪个 CSS 选择器应该覆盖所有其他 CSS 选择器的算法。在下面的例子中,article p比单独的 p 更具体,所以<article>中的所有段落元素都将是灰色的:


article p { color: #444; }p { color: #000; }
复制代码


:is()的情况下,是在其参数中找到的最具体的选择器。在:where()的情况下,特异性为零。


请看以下 CSS:


article p {  color: black;}
:is(article, section, aside) p { color: red;}
:where(article, section, aside) p { color: blue;}
复制代码


基于此 CSS 应用于以下 HTML:


<article>  <p>paragraph text</p></article>
复制代码


最终段落文本将显示为红色,如下面的 CodePen 演示所示。



:is()选择器与article p有着相同的特性,但是它在样式表中的后面出现,所以文本变成红色。如果要应用蓝色,必须同时删除article p:is()选择器,因为:where()选择器对比这两个选择器都不那么具体。


更多的代码库将使用√而不是:where()。然而,:where()的零特异性对于 CSS 重置可能是实用的,当没有特定的样式可用时,CSS 重置应用标准样式的基线。通常,重置会应用默认字体、颜色、填充和边距。


这段 CSS 重置代码将 1em 的上边距应用于<h2>标题,除非它们是<article>元素的第一个子元素:


h2 {  margin-block-start: 1em;}
article :first-child { margin-block-start: 0;}
复制代码


稍后尝试在样式表中设置自定义<h2>上边距没有效果,因为article :first-child具有更高的特异性:


h2 {  margin-block-start: 2em;}
复制代码


可以使用更高特异性的选择器来修复这个问题,但它需要更多的代码,而且对其他开发人员来说并不一定是显而易见的。你最终会忘记你为什么需要它:


article h2:first-child {  margin-block-start: 2em;}
复制代码


您也可以通过将!important 应用到每种样式来解决问题,但请避免这样做!它使得进一步的造型和开发更具挑战性:


h2 {  margin-block-start: 2em !important;}
复制代码


一个更好的选择是在 CSS 重置中采用:where()的零特异性:


/* reset */:where(h2) {  margin-block-start: 1em;}
:where(article :first-child) { margin-block-start: 0;}
复制代码


你现在可以覆盖任何 CSS 重置样式,而不管它的特殊性,也不需要更多的选择器或!important


h2 {  margin-block-start: 2em;}
复制代码

:has 伪类选择器

:has()选择器使用与:is():where()相似的语法,但它的目标是包含一组其他元素的元素。例如,下面的 CSS 用于为任何包含一个或多个<a><img>标签的<section>链接添加一个蓝色的两像素边框:


a:has(img, section) {  border: 2px solid blue;}
复制代码


这是几十年来最令人兴奋的 CSS 开发!开发人员终于有了一种针对父元素的方法!


难以捉摸的“父选择器”一直是最受欢迎的 CSS 功能之一,但它增加了浏览器供应商的性能复杂性,因此已经很长时间了。简单地说:


  • 浏览器在页面上绘制元素时将 CSS 样式应用于元素。因此,在添加更多子元素时,必须重新绘制整个父元素。

  • 在 JavaScript 中添加、删除或修改元素可能会影响整个页面的样式,直到包含的<body>


假设供应商已经解决了性能问题,:has()的引入允许了过去没有 JavaScript 就不可能实现的可能性。例如,当任何必需的内部字段无效时,您可以设置外部表单<fieldset>和以下提交按钮的样式:


/* red border when any required inner field is invalid */fieldset:has(:required:invalid) {  border: 3px solid red;}
/* change submit button style when invalid */fieldset:has(:required:invalid) + button[type='submit'] { opacity: 0.2; cursor: not-allowed;}
复制代码



此示例添加包含子菜单项列表的导航链接子菜单指示符:


nav li:has(ol, ul) a::after {  display: inlne-block;  content: ">";}
复制代码


或者你可以添加调试风格,比如突出显示所有的<figure>元素,而不带内部的img


figure:not(:has(img)) {  border: 3px solid red;}
复制代码



在你进入你的编辑器并重构你的 CSS 代码库之前,请注意:has()是新的,支持比:is():where()更有限。它可以在 Safari 15.4+和 Chrome 101+中使用,但它应该在 2023 年之前广泛使用。

最后

:is():where()伪类选择器简化了 CSS 语法。您将不需要嵌套和 CSS 预处理器(尽管这些工具提供了其他好处,如部分、循环和缩小)。


:has()更具革命性和令人兴奋。父级选择将迅速流行起来,我们将忘记过去的黑暗时代!


看完本文如果觉得有用,记得点个赞支持,收藏起来说不定哪天就用上啦~


专注前端开发,分享前端相关技术干货,公众号:南城大前端(ID: nanchengfe)

发布于: 2023-07-10阅读数: 30
用户头像

南城FE

关注

还未添加个人签名 2019-02-12 加入

专注前端开发,分享前端知识

评论

发布
暂无评论
CSS is、where和has选择器_CSS_南城FE_InfoQ写作社区