Overview
CSS selectors allow you to select elements by type, attribute, and position in the HTML document. This tutorial describes three new options:is()
,:where()
, and:has()
.
Selectors are often used in style sheets. The following example finds all <p>
paragraph elements and changes the weight to bold:
p {
font-weight: bold;
}
You can also use selectors in JavaScript to find DOM nodes:
- document.querySelector() returns the first matching HTML element.
- document.querySelectorAll() returns all matching HTML elements in an array-like NodeList .
Pseudo-class selectors locate HTML elements based on their current state. Perhaps the most well known is :hover
which applies styles when the cursor is over an element, so it is used to highlight clickable links and buttons.
Other popular options include:
-
:visited
: matches visited links -
:target
: matches the element pointed to by the document URL -
:first-child
: points to the first child element -
:nth-child
: Select the specified child element -
:empty
: matches elements with no content or child elements -
:checked
: matches a checked checkbox or radio button -
:blank
: Select the input box where the user input is empty -
:enabled
: matches an enabled input box. An element is enabled if it can be activated (such as selected, clicked, or accepting text input), or can receive focus -
:disabled
: matches a disabled input box. Element cannot be activated or gain focus when disabled -
:required
: Point to the required input box. The so-called required means that the user must specify a value for the input box before submitting the form to which it belongs -
:valid
: Match a content to verify the correct input box -
:invalid
: match an input box whose content fails validation -
:playing
: Points to a playingaudio
orvideo
element
Browsers have recently received three more pseudo-class selectors...
:is pseudo-class selector
Note: This was originally specified as :matches()
and :any()
, but :is()
has become a CSS standard.
MDN explains: :is()
CSS pseudo-class function takes a selector list as a parameter, and selects any element that can be selected by any selector in the list. This is useful for writing large selectors in a more compact form.
You often need to apply the same style to more than one element.比如说, <p>
0360a03e3a91975702bf0936c1b20b41---段落文本颜色默认为黑色,但是当它位于<article>
, <section>
, <aside>
时,文本颜色为grey:
/* default black */
p {
color: #000;
}
/* gray in <article>, <section>, or <aside> */
article p,
section p,
aside p {
color: #444;
}
This is a simple example, but more complex pages will result in more complex and verbose selector strings. Syntax errors in any selector will break the styles of all elements.
CSS preprocessors like Sass allow nesting (this will also be present in native CSS ).
article, section, aside {
p {
color: #444;
}
}
This creates the same CSS code, reduces typing effort, and prevents errors. but:
- Before native nesting came along, you still needed a CSS builder. You might want to use a solution like Sass, but this might introduce complexity for some development teams.
- Nesting can cause other problems. Building deeply nested selectors is easy, but it becomes increasingly difficult to read and output verbose CSS.
:is()
provides a native CSS solution. This feature is supported by all modern browsers (except IE).
:is(article, section, aside) p {
color: #444;
}
A single selector can contain any number of :is()
pseudo-classes. For example, the complex selector below applies the green text color to all <h1>
, <h2>
and <p>
elements which are <section>
, which contains the class .primary
or .secondary
and is not the first child of <article>
.
article section:not(:first-child):is(.primary, .secondary) :is(h1, h2, p) {
color: green;
}
The equivalent code without :is()
requires six CSS selectors.
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;
}
Note that :is()
cannot match ::before
and ::after
pseudo-elements, so the following example code will not work:
/* NOT VALID - selector will not work */
div:is(::before, ::after) {
display: block;
content: '';
width: 1em;
height: 1em;
color: blue;
}
:where pseudo-class selector
:where()
The selector syntax is the same as :is()
and is also supported by all modern browsers (except IE). This tends to result in the same style. for example:
:where(article, section, aside) p {
color: #444;
}
The difference is in priority . Priority is the algorithm used to decide which CSS selector should override all other selectors.在下面的例子中, article p
比p
具体, <article>
内的p
元素的字体颜色将will be grey:
article p { color: #444; }
p { color: #000; }
In the case of :is()
the priority is the most specific selector found in its argument. In the case of :where()
the priority is zero.
Consider the following CSS:
article p {
color: black;
}
:is(article, section, aside) p {
color: red;
}
:where(article, section, aside) p {
color: blue;
}
Let's apply this CSS to the following HTML:
<article>
<p>paragraph text</p>
</article>
Paragraph text will be rendered in red, click the link to view the CodePen example .
:is()
selector has the same priority as article p
, but it's after the stylesheet, so the text turns red. If necessary, remove both article p
and :is()
selectors to apply blue, because the :where()
selector has lower priority than both.
More codebases will use :is()
instead of :where()
. However, the zero priority of :where()
is useful for CSS resets, which apply a baseline of standard styles in the absence of specific styles. Normally, a reset applies a default font, color, padding, and margins.
This CSS reset code pair <h2>
applies the top margin of 1em
28c23e9c7a93a21f6f3431f1d8f33752--- unless they are the first child of the <article>
element.
/* CSS reset */
h2 {
margin-block-start: 1em;
}
article :first-child {
margin-block-start: 0;
}
Trying to set a custom <h2>
top margin after the stylesheet has no effect, because article :first-child
has higher priority:
/* never applied - CSS reset has higher specificity */
h2 {
margin-block-start: 2em;
}
You could fix this with a higher priority selector, but that would require more code and not be obvious to other developers. You will eventually forget why you need it.
/* styles now applied */
article h2:first-child {
margin-block-start: 2em;
}
You can also fix this by applying !important
to each style, but please avoid this! It makes further style definition and development more challenging.
/* works but avoid this option! */
h2 {
margin-block-start: 2em !important;
}
A better option is to have a zero priority of :where()
in your CSS reset.
/* reset */
:where(h2) {
margin-block-start: 1em;
}
:where(article :first-child) {
margin-block-start: 0;
}
Now, you can override any CSS reset style, regardless of its priority; no further selectors or !important
are required:
/* now works! */
h2 {
margin-block-start: 2em;
}
:has() pseudo-class selector
:has()
selector uses syntax similar to :is()
and :where()
, but it targets an element that contains other elements. For example, here's the CSS to add a blue, two-pixel wide border to any <a>
link that contains one or more <img>
or <section>
tags:
/* style the <a> element */
a:has(img, section) {
border: 2px solid blue;
}
This is the most exciting CSS advancement in decades! Developers finally have a way to target parent elements.
The elusive "parent selector" has been one of the most requested CSS features, but it caused performance headaches for browser vendors. So it's been around for a long time. in short:
- The browser applies CSS styles to an element when it is drawn on the page. So when further child elements are added, the entire parent element has to be repainted.
- Adding, removing or modifying elements in JavaScript may affect the style of the entire page, up to the closing
<body>
tag.
The introduction of :has()
allows what used to be impossible without JavaScript, assuming the vendor has addressed the performance issues. For example, you can style the outer form <fieldset>
and the submit button below when any of the required inner fields fail validation.
/* 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;
}
This example adds a navigation link submenu indicator with a list of submenu items:
/* display sub-menu indicator */
nav li:has(ol, ul) a::after {
display: inlne-block;
content: ">";
}
Alternatively, you can add debug styles such as highlighting all <figure>
elements that do not have an inner img
.
/* where's my image?! */
figure:not(:has(img)) {
border: 3px solid red;
}
Before opening your editor and refactoring your CSS codebase, please note that :has()
is very new and has more limited support than :is()
and :where()
. It's available in Safari 15.4+ and Chrome 105+ , but should be widely available by 2023.
Summarize
:is()
and :where()
pseudo-class selectors simplify CSS syntax. Your need for nesting and CSS preprocessors will be reduced.
:has()
More revolutionary and exciting. Parenting options will quickly catch on and we will forget about the dark ages.
The above is all the content of this article. If it is helpful to you, please like, collect and forward~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。