likes
comments
collection
share

聊聊那些你可能不知道的 CSS 选择器

作者站长头像
站长
· 阅读数 11

关于 CSS 发展的一些思考

到今天为止,CSS3 在很多人的印象中似乎还是一个比较新的技术名词,其中很多模块的发展也很难引起大家的关注。但是 CSS3 可不是新东西,这个技术的发展从上个世纪就开始了。原因是什么呢?毫无疑问,是因为 JavaScript 的发展过于迅猛,进而催生出一堆以 SCSS/LESS 为代表的 CSS 预编译器工具。由于这类工具太好用了,反而大家的注意力就从 CSS 技术本身上迁移到 CSS 工具上面了。想想你有多久没有写过传统的 CSS 了呢?同时因为 All in JS 的趋势,很多样式相关的代码都被迁移到 JavaScript 中,比如 React 生态中以 StyledComponent 为代表的 CSS-in-JS 框架。那不用 CSS 写 CSS,反而使用 JavaScript 写 CSS 有什么缺点呢?有人认为性能不好;有人认为调试样式变得复杂;还有人认为职责不够分明。不管有多少不好,还是有人用,而且有大量的人用。这个道理其实也用在 UI 框架中,WebComponents 的发展很少有人关注,但是像 React 这类框架的一举一动却影响着无数的开发者。计算机领域有个名言:永远不要为了使用一个技术而用另一个技术。但是现在似乎不好用了。不过没关系,我们只要都去学就可以了。React 要学,ECMAScript Next 也要学。TailwindCSS 要学,CSS3、CSS4 也要学。这并不冲突。

CSS3 选择器

言归正传,今天这篇文章就来聊聊 CSS3 和 CSS4 中的几个比较新的选择器。CSS 选择器自然都不会陌生,这也是所有人学习 CSS 最先接触到的知识之一。CSS3 以来,选择器越来越复杂了,不再仅仅是 class 选择器,标签选择器,父子选择器等基础的选择器。

is 伪类选择器

这个选择器最早不叫作 is,而是叫 metches 或者是 any,但是现在已经被确定为 is。我们通过案例来看它的作用。我们设置 p 的默认颜色为黑色,但是在 main、header 和 footer 中为绿色。原生 CSS 需要这么写:

/* p 标签默认样式 */
p {
  color: black;
}

/* p 标签在 main header 和 footer 中的样式 */
main p,
header p,
footer p {
  color: green;
}

如果你用 SASS,它支持嵌套,所以可以应该这么写:

main, header, footer {
  p {
    color: green;
  }
}

这样会让代码变得简洁。如果我们使用 is 选择器来解决这个问题的话,可以这么写:

:is(main, header, footer) p {
  color: green;
}

is 支持所有的现代选择器,除了那么马上死掉的 IE。像一些更复杂的情况,比如这样:

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: red;
}

我们使用 is 可以让它们变得更加简单。

article section:not(:first-child):is(.primary, .secondary) :is(h1, h2, p) {
  color: red;
}

但是需要注意 is 不能匹配伪元素。像下面这样是跑不起来的。

p:is(::before, ::after) {
  display: block;
  content: 'pseudo';
}

where 伪类选择器

where 和 is 一样被所有现代浏览器支持,并且通常会和 is 有相同的作用,比如这样:

:where(main, header, footer) p {
  color: green;
}

但是它们肯定不会完全一样,不然就没必要存在两个了。它们的区别在于 is 会提高选择器权重,但是 where 不会提高权重。像下面这个例子:

main p {
  color: black;
}

:is(main, header, footer) p {
  color: green;
}

:where(main, header, footer) p {
  color: blue;
}

虽然 where 在后面,但是由于 is 会让权重变高,所以 p 的颜色还是绿色。所以 where 的优势是可以覆盖任何样式,也可以被任何样式覆盖,不需要使用权重更高的选择器或者 !important。

has 伪类选择器

has 选择器需要有一个目标元素,后面的参数是它的父元素。这是目前唯一一种可以选择父元素的选择器。下面是给所有包含 img 和 div 的 a 标签设置边框的用法:

a:has(img, div) {
  border: 2px solid blue;
}

它还支持更复杂的用法,比如在表单按钮中:

/* 必填字段无效时设置红色边框 */
fieldset:has(:required:invalid) {
  border: 3px solid red;
}

/* 必填字段无效时禁用提交按钮 */
fieldset:has(:required:invalid) + button[type='submit'] {
  opacity: 0.2;
  pointer-events: none;
}

在过去实现这种功能必须通过 JavaScript 的能力,但是现在不需要了。不过 has 选择器的支持度不如 is 和 where,Chrome 和 Safari 只提供了有限的支持,预计会在 2022 年底全面支持。