CSS 选择器允许您按类型、属性或在 HTML 文档中的位置选择元素。本教程介绍了三个新选项 --- :is()、:where() 和 :has()。
选择器通常用于样式表中。以下示例定位所有<p>
段落元素并将字体粗细更改为粗体:
p {
font-weight: bold;
}
您还可以在 JavaScript 中使用选择器来定位 DOM 节点:
-
document.querySelector()返回第一个匹配的 HTML 元素
-
document.querySelectorAll()在类似数组的NodeList中返回所有匹配的 HTML 元素
伪类选择器根据 HTML 元素的当前状态来定位它们。也许最著名的是:hover
,它会在光标移到某个元素上时应用样式,因此它用于突出显示可点击的链接和按钮。其他受欢迎的选择包括:
-
:visited
: 匹配访问过的链接 -
:target
: 匹配文档 URL 所定位的元素 -
:first-child
: 以第一个子元素为目标 -
:nth-child
:选择特定的子元素 -
:empty
: 匹配没有内容或子元素的元素 -
:checked
: 匹配一个打开的复选框或单选按钮 -
:blank
: 设计一个空的输入字段 -
:enabled
: 匹配启用的输入字段 -
:disabled
: 匹配禁用的输入字段 -
:required
: 以必填输入字段为目标 -
:valid
: 匹配一个有效的输入字段 -
:invalid
: 匹配无效的输入字段 -
:playing
:定位正在播放的音频或视频元素
浏览器最近又收到了三个伪类选择器......
CSS :is 伪类选择器 {#thecssispseudoclassselector}
注意:这最初指定为:matches()
and :any()
,但:is()
已成为 CSS 标准。
您经常需要对多个元素应用相同的样式。例如,<p>
段落文本默认为黑色,但出现在<article>
、<section>
或中时为灰色<aside>
:
/* default black */
p {
color: #000;
}
/* gray in <article>, <section>, or <aside> */
article p,
section p,
aside p {
color: #444;
}
这是一个简单的示例,但更复杂的页面将导致更复杂和冗长的选择器字符串。任何选择器中的语法错误都可能破坏所有元素的样式。
Sass 等 CSS 预处理器允许嵌套(这也将用于原生 CSS):
article, section, aside {
p {
color: #444;
}
}
这会创建相同的 CSS 代码,减少打字工作,并可以防止错误。但:
-
在本机嵌套到来之前,您将需要一个 CSS 构建工具。您可能想使用像 Sass 这样的选项,但这会给一些开发团队带来复杂性。
-
嵌套会导致其他问题。很容易构建深度嵌套的选择器,这些选择器变得越来越难以阅读和输出冗长的 CSS。
:is()
提供了一个原生 CSS 解决方案,它在所有现代浏览器(不是 IE)中都有全面支持:
:is(article, section, aside) p {
color: #444;
}
单个选择器可以包含任意数量的:is()
伪类。例如,下面的复杂选择器将绿色文本颜色应用到所有<h1>
、<h2>
和<p>
元素,这些元素是 a 的子元素<section>
,具有类.primary
or.secondary
并且不是 an 的第一个子元素<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
元素,所以这个示例代码会失败:
/* NOT VALID - selector will not work */div:is(::before, ::after) {
display: block;
content: '';
width: 1em;
height: 1em;
color: blue;}
CSS :where 伪类选择器 {#thecsswherepseudoclassselector}
:where()
选择器语法与所有现代浏览器(非 IE)相同:is()
并且也受支持。它通常会导致相同的样式。例如:
:where(article, section, aside) p {
color: #444;
}
不同之处在于特异性。特异性是用于确定哪个 CSS 选择器应该覆盖所有其他选择器的算法。在下面的例子中,article p
is 比p
单独的更具体,因此 an 中的所有段落元素都<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>
选择:is()
器与 具有相同的特异性article p
,但它出现在样式表的后面,因此文本变为红色。有必要同时删除article p
和:is()
选择器以应用蓝色,因为:where()
选择器不如其中任何一个具体。
更多的代码库将使用:is()
比:where()
. 然而,零特异性:where()
对于 CSS 重置可能是实用的,它在没有特定样式可用时应用标准样式的基线。通常,重置应用默认字体、颜色、填充和边距。
此 CSS 重置代码将上边距应用于1em
标题<h2>
,除非它们是元素的第一个子元素<article>
:
/* CSS reset */
h2 {
margin-block-start: 1em;
}
article :first-child {
margin-block-start: 0;
}
稍后在样式表中尝试设置自定义<h2>
上边距无效,因为它article :first-child
具有更高的特异性:
/* never applied - CSS reset has higher specificity */
h2 {
margin-block-start: 2em;
}
您可以使用更高特异性的选择器来解决这个问题,但它的代码更多,而且对其他开发人员来说不一定显而易见。你最终会忘记你为什么需要它:
/* styles now applied */
article h2:first-child {
margin-block-start: 2em;
}
!important
您也可以通过应用于每种样式来解决问题,但请避免这样做!它使进一步的样式和开发更具挑战性:
/* works but avoid this option! */
h2 {
margin-block-start: 2em !important;}
:where()
更好的选择是在您的 CSS 重置中采用零特异性:
/* reset */
:where(h2) {
margin-block-start: 1em;
}
:where(article :first-child) {
margin-block-start: 0;
}
您现在可以覆盖任何 CSS 重置样式,而不管其具体性如何;不需要更多的选择器或!important
:
/* now works! */
h2 {
margin-block-start: 2em;
}
CSS :has 伪类选择器 {#thecsshaspseudoclassselector}
选择器使用与and:has()
类似的语法,但它以包含一组其他元素的元素为目标。例如,下面是用于向任何包含一个或多个or标签的链接添加蓝色、两像素边框的 CSS::is()``:where()``<a>``<img>``<section>
/* style the <a> element */
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;
}
此示例添加一个包含子菜单项列表的导航链接子菜单指示器:
/* display sub-menu indicator */
nav li:has(ol, ul) a::after {
display: inlne-block;
content: ">";
}
或者您可以添加调试样式,例如突出显示所有<figure>
没有 inner 的元素img
:
/* where's my image?! */
figure:not(:has(img)) {
border: 3px solid red;
}
在你进入你的编辑器并重构你的 CSS 代码库之前,请注意它:has()
是新的并且支持比:is()
和更有限:where()
。它在Safari 15.4+和 Chrome 101+ 中以实验性标志提供,但到 2023 年应该可以广泛使用。
选择器总结 {#selectorsummary}
和伪类选择器简化了 CSS 语法:is()
。:where()
您将不再需要嵌套和 CSS 预处理器(尽管这些工具提供了其他好处,例如局部、循环和缩小)。
:has()
更具革命性和令人兴奋。父母选择将迅速流行,我们将忘记黑暗时代!:has()
当所有现代浏览器都可用时,我们将发布完整教程。