On logical selector — the parent selector is coming!

Time:2022-8-2

In the CSS selector family, a relatively new selector is added–Logic selectorAt present, there are 4 members:

  • :is
  • :where
  • :not
  • :has

This article will lead you to understand and deepen them. Make learning practical and write more modern selectors.


: is pseudo class selector

:is()The CSS pseudo class function takes the selector list as a parameter and selects the elements that can be selected by any selector in the list.

Previously, for some common style settings of the same child element of multiple different parent containers, the following CSS code may appear:

header p:hover,
main p:hover,
footer p:hover {
  color: red;
  cursor: pointer;
}

And now there is:is()Pseudo class, the above code can be rewritten as:

:is(header, main, footer) p:hover {
  color: red;
  cursor: pointer;
}

It does not implement a new function of a selector, but is more like a syntax sugar, which is similar to the class () syntax in JavaScript ES6. It only repackages the original function, realizes the syntax of expressing an operation more easily, and simplifies the writing of some complex code.

Grammatical sugar(synchronous sugar) refers to the syntax that can more easily express an operation in a programming language. It can make it easier for programmers to use the language, and the operation can become clearer and more convenient, or more in line with the programming habits of programmers. To understand in a more understandable way is to change a writing method on the basis of a previous grammar. The functions are the same, but the writing method is different, mainly to make it easier for developers to understand in the process of use.

Yitusheng foreword (quoted toNew CSS functional pseudo-class selectors :is() and :where()):

On logical selector -- the parent selector is coming!

Support multi-layer stacking and continuous use

Take another look at this situation. The original CSS code is as follows:

<div><i>div i</i></div>
<p><i>p i</i></p>
<div><span>div span</span></div>
<p><span>p span</span></p>
<h1><span>h1 span</span></h1>
<h1><i>h1 i</i></h1>

If you want to add the above HTML,<div>and<p>Down<span>and<i>The color of CSS is set to red. Normal CSS may be as follows:

div span,
div i,
p span,
p i {
    color: red;
}

Yes:is()After, the code can be simplified as:

:is(div, p) :is(span, i) {
    color: red;
}

The results are as follows:

On logical selector -- the parent selector is coming!

Here, it also supports:is()For cascading. adopt:is(div, p) :is(span, i)The above four rows of selectors can be combined to achieve the same effect.

Of course, this example is relatively simple. I can’t see it:is()The power of. The following example is obvious, such a large section of CSS selector code:

ol ol ul,     ol ul ul,     ol menu ul,     ol dir ul,
ol ol menu,   ol ul menu,   ol menu menu,   ol dir menu,
ol ol dir,    ol ul dir,    ol menu dir,    ol dir dir,
ul ol ul,     ul ul ul,     ul menu ul,     ul dir ul,
ul ol menu,   ul ul menu,   ul menu menu,   ul dir menu,
ul ol dir,    ul ul dir,    ul menu dir,    ul dir dir,
menu ol ul,   menu ul ul,   menu menu ul,   menu dir ul,
menu ol menu, menu ul menu, menu menu menu, menu dir menu,
menu ol dir,  menu ul dir,  menu menu dir,  menu dir dir,
dir ol ul,    dir ul ul,    dir menu ul,    dir dir ul,
dir ol menu,  dir ul menu,  dir menu menu,  dir dir menu,
dir ol dir,   dir ul dir,   dir menu dir,   dir dir dir {
  list-style-type: square;
}

Available:is()Optimized as:

:is(ol, ul, menu, dir) :is(ol, ul, menu, dir) :is(ul, menu, dir) {
  list-style-type: square;
}

Pseudo elements are not supported

There is a special case that cannot be used:is()To select::beforeand::afterTwo pseudo elements. For example:

Note that only pseudo elements and pseudo classes are not supported, such as:focus:hoverYes.

div p::before,
div p::after {
    content: "";
    //...
}

Cannot be written as:

div p:is(::before, ::after) {
    content: "";
    //...
}

:isPriority of selector

Look at such an interesting situation:

<div>
    <p class="test-class" id="test-id">where & is test</p>
</div>
<div>
    <p class="test-class">where & is test</p>
</div>

We bring.test-classElement, set a default color:

div .test-class {
    color: red;
}

If, at this time, we introduce:is()Match:

div :is(p) {
    color: blue;
}

At this time, due todiv :is(p)Can be seen asdiv p, priority is nodiv .test-classHigh, therefore, the color of the selected text will not change.

But if we are:is()In the selector, add one#test-id, the situation is different.

div :is(p, #text-id) {
    color: blue;
}

It is understood that if the above selector is split, the above code can be split into:

div p {
    color: blue;
}
div #text-id {
    color: blue;
}

Well, we have reason to guess that with#text-idof<p>With a higher priority selector, the color of the element will becomeblue, and the other onediv pBecause the priority is not high enough, the first paragraph of the text is stillgreen

But here, miraculously, both texts becomeblue

On logical selector -- the parent selector is coming!

CodePen Demo — the specificity of CSS :is selector

This is because,:is()The priority of is determined by the selector with the highest priority in its selector list. We can’t cut them apart.

aboutdiv :is(p, #text-id)is:()There is an ID selector inside, so all elements matched by this rule will be applieddiv #idSelector priority at this level. This is very important. Again, for:is()We can’t separate the priorities of selectors. They are a whole, and the priority depends onThe selector with the highest priority in the selector list

Alias of: is: matches() and: any()

:is()It is the latest standard naming. Before, there have been choices with the same function, which are:

:is(div, p) span {}
//Equivalent to
:-webkit-any(div, p) span {}
:-moz-any(div, p) span {}
:matches(div, p) span {}

Of course, the following three have been abandoned, and it is not recommended to continue to use them. And today (2022-04-27):is()The compatibility of is already very good. If you don’t need to be compatible with IE series, you can consider starting to use it (cooperate withautoprefixer), lookCanIUse

On logical selector -- the parent selector is coming!

: where pseudo class selector

Got it:isAfter that, we can have a look again:where, the two of them have a very strong correlation.:whereIt also takes the selector list as its parameter and selects any element that can be selected by one of the selectors in the list.

Another example:

:where(header, main, footer) p:hover {
  color: red;
  cursor: pointer;
}

The above code uses:where, which can be roughly seen as:

header p:hover,
main p:hover,
footer p:hover {
  color: red;
  cursor: pointer;
}

That’s interesting. It’s not the same as above:isIs it the same?

So what is the difference between them?

:isand:whereDifferences between

First, grammatically,:isand:whereIt’s exactly the same. Their core difference ispriority

Take an example:

<div>
    <p>where & is test</p>
</div>

CSS code is as follows:

:is(div) p {
    color: red;
}
:where(div) p {
    color: green;
}

Normally, according to our understanding,:is(div) pand:where(div) pCan be converted intodiv p, because:where(div) pAfter definition, so the color of the text should begreenGreen, however, the actual color iscolor: redRed:

On logical selector -- the parent selector is coming!

This is because,:where()and:is()The difference is that,:where()Priority ofAlways 0, but:is()The priority of is determined by the selector with the highest priority in its selector list.

The above example is not particularly obvious. Let’s make a slight Transformation:

<div id="container">
    <p>where & is test</p>
</div>

We add an ID attribute to div and modify the above CSS code:

:is(div) p {
    color: red;
}
:where(#container) p {
    color: green;
}

Even so, because:where(#container)The priority of is 0, so the color of the text is still red.:where()Priority ofAlways 0This point needs to be kept in mind in the process of use.

Combination, nesting

A very big feature of CSS selectors is composition and nesting.:isand:whereIt is no exception. Therefore, they can also be combined and nested with each other. The following CSS selectors are reasonable:

/*Combination*/
:is(h1,h2) :where(.test-a, .test-b) {
  text-transform: uppercase;
}
/*Nesting*/
.title:where(h1, h2, :is(.header, .footer)) {
  font-weight: bold;
}

Here is a brief summary,:isand:whereAre very good grouping logic selectors, the only difference is:where()Priority ofAlways 0, and:is()The priority of is determined by the selector with the highest priority in its selector list.

: not pseudo class selector

Now let’s introduce some very useful:notPseudo class selector.

:notPseudo class selectors are used to match elements that do not match a set of selectors. Because its function is to prevent specific elements from being selected, it is also called negation pseudo class.

For example, the HTML structure is as follows:

<div class="a">div.a</div>
<div class="b">div.b</div>
<div class="c">div.c</div>
<div class="d">div.d</div>
div:not(.b) {
    color: red;
}

div:not(.b)It can be selected except that class is.bAll div elements except element:

On logical selector -- the parent selector is coming!

MDN error example? An interesting phenomenon

Interestingly, it is introduced in MDN:notFor example:

/* Selects any element that is NOT a paragraph */
:not(p) {
  color: blue;
}

intend,:not(p)You can choose any not<p>Element of the label. However, the CSS selector above, in the following HTML structure, the measured results are not quite right.

<p>p</p>
<div>div</div>
<span>span</span>
<h1>h1</h1>

The results are as follows:

On logical selector -- the parent selector is coming!

intend,:not(p)Can still be selected<p>Element. I tried several browsers and got the same results.

CodePen Demo — :not pesudo demo

Why is this? This is due to:not(p)Can also be selected<body>, then<body>The color of becomesblue, becausecolorIs an inheritable attribute,<p>The tag inherits<body>The color attribute of the resulting<p>It’s also blue.

Let’s change it into a non inheritable attribute and try it:

/* Selects any element that is NOT a paragraph */
:not(p) {
  border: 1px solid;
}

On logical selector -- the parent selector is coming!

OK, this time<p>No border reflection, no problem! When actually using, you need to pay attention to the inheritance of this layer!

: priority issues for not

Here are some uses:notProblems needing attention.

:not:is:whereUnlike other pseudo classes, these pseudo classes do not increase the priority of selectors. Its priority is the priority of its parameter selector.

And, inCSS Selectors Level 3:not()Only a single selector is supported in, and fromCSS Selectors Level 4Start,:not()Multiple selectors are supported internally, such as:

/*CSS selectors Level 3,: Notif there are multiple values inside, they need to be separated*/
p:not(:first-of-type):not(.special) {
}
/*CSS selectors level 4 supports comma separation*/
p:not(:first-of-type, .special) {
}

And:is()similar,:not()The selector itself does not affect the priority of the selector, which is determined by the selector with the highest priority in its selector list.

: not (*) problem

use:not(*)Will match any non element element, so this rule will never be applied.

It’s equivalent to a meaningless piece of code.

: not() cannot be nested: not()

No dolls.:notPseudo classes are not allowed to be nested, which means:not(:not(...))Is invalid.

: not() actual combat analysis

So, what are the particularly interesting application scenarios of: not()? Let me list one here.

stayW3 CSS selectors-4 specificationIn, a very interesting new one is added:focus-visiblePseudo class.

:focus-visibleThis selector can effectively display different forms of focus according to the user’s input mode (mouse vs keyboard).

With this pseudo class, it can be achieved that when the user uses the mouse to operate the focusable element, it will not be displayed:focusStyle or make it weak, and when the user uses the keyboard to operate the focus, use:focus-visible, so that the focus elements can obtain a strong performance style.

Look at a simple demo:

<button>Test 1</button>
button:active {
  background: #eee;
}
button:focus {
  outline: 2px solid red;
}

Click with the mouse:

On logical selector -- the parent selector is coming!

It can be seen that when clicking with the mouse, the element’s:activePseudo class, also triggered:focusPseudo class, not very beautiful. But if it is setoutline: noneIt will make the experience of keyboard users very bad. Because when keyboard users use tab to try to switch focus, they willoutline: noneAt a loss.

Therefore, you can use:focus-visiblePseudo class transformation:

button:active {
  background: #eee;
}
button:focus {
  outline: 2px solid red;
}
button:focus:not(:focus-visible) {
  outline: none;
}

To see the effect, click the button with the mouse and click the button with the keyboard to control the focus:

On logical selector -- the parent selector is coming!

CodePen Demo — :focus-visible example

It can be seen that clicking with the mouse will not trigger:foucs, only when the keyboard operates the focus element and uses tab to switch the focus,outline: 2px solid redThis code will take effect.

In this way, we not only ensure the click experience of normal users, but also ensure the focus management experience of users who cannot use the mouse, and we have made great efforts in accessibility.

It is worth noting why it is used herebutton:focus:not(:focus-visible)It’s written in this way instead of directly:

button:focus {
  outline: unset;
}
button:focus-visible {
  outline: 2px solid red;
}

Explain,button:focus:not(:focus-visible)Means,The button element triggers the focus state, and it is not triggered through focus visible, understood as supporting:focus-visibleBrowser, activated by mouse:focusIn this case, no setting is requiredoutline

For compatibility not supported:focus-visibleBrowser, when:focus-visibleWhen incompatible, you still need to have:focusThe existence of pseudo classes.

Therefore, with the help of:not()Pseudo class, cleverly achieved a practical effect of the program degradation.

It’s a little windy here, so we need to understand it well.

: not compatibility

Experienced two versions of CSS selectors Level 3 and CSS selectors level 4. Today (2020-05-04), except for IE series,:notThe compatibility of has been very good:

On logical selector -- the parent selector is coming!

: has pseudo class selector

OK。 Finally, the heaviest of all logic selectors:hasHere we go. The reason why it is important is that its birth fills the gap in the previous CSS selectors, which has no real core meaningParent selectorThe vacancy of.

:hasThe pseudo class accepts a selector group as a parameter, which is relative to the:scopeMatch at least one element.

Take an example:

<div>
    <p>div -- p</p>
</div>
<div>
    <p class="g-test-has">div -- p.has</p>
</div>
<div>
    <p>div -- p</p>
</div>
div:has(.g-test-has) {
    border: 1px solid #000;
} 

We passeddiv:has(.g-test-has)Selector, which means that class under div is selected as.g-test-hasDiv element of.

Note that the choice here is not:has()The selector inside the package selects the element, but uses:has()The host element of the pseudo class.

The effect is as follows:

On logical selector -- the parent selector is coming!

You can see that the class under the second div is.g-test-hasSo the second div is added with border.

: has () parent selector — parent element selection of nested structure

Let’s deepen our impression through a few demo.:has()It can also be more complicated.

<div>
    <span>div span</span>
</div>

<div>
    <ul>
        <li>
            <h2><span>div ul li h2 span</span></h2>
        </li>
    </ul>
</div>

<div>
    <h2><span>div h2 span</span></h2>
</div>
div:has(>h2>span) {
    margin-left: 24px;
    border: 1px solid #000;
}

Here, it is required to accurately select the div element that the direct child element under div is H2, and the direct child element under H2 has the div element of span. Note that the top level selected uses the parent element div of: has (). The results are as follows:

On logical selector -- the parent selector is coming!

What is embodied here isNested structure to accurately find the corresponding parent element

: has () parent selector — sibling element selection of sibling structure

There is another case, which was also difficult to deal with before, that is, the sibling element selection of peer structure.

Look at this demo:

<div class="has-test">div + p</div>
<p>p</p>

<div class="has-test">div + h1</div>
<h1>h1</h1>

<div class="has-test">div + h2</div>
<h2>h2</h2>

<div class="has-test">div + ul</div>
<ul>ul</ul>

We want to find the hierarchical relationship between brothers, followed by<h2>Elemental.has-testElement, which can be written as follows:

.has-test:has(+ h2) {
    margin-left: 24px;
    border: 1px solid #000;
}

The effect is as follows:

On logical selector -- the parent selector is coming!

What is embodied here isSibling structure, accurately find the corresponding pre sibling element

In this way, CSS has not implemented the parent selector for a long time:has()At the beginning, it can be done. This selector can greatly improve the development experience and solve the problem that requires a lot of JavaScript code before it can be completed.

For the above demo summary, you can stamp hereCodePen Demo — :has Demo

: has() compatibility, give time

Unfortunately,:has()In the recentSelectors Level 4It is determined in the specification that the current compatibility is still relatively poor. As of 2022-05-04, Safari and the latest version of chrome (V101) can be openedExperimental Web Platform featuresExperience)

On logical selector -- the parent selector is coming!

To open this feature in chrome, 1. Enter the browser URL box chrome://flags , 2. Open \enable-experimental-web-platform-features

Wait patiently, give time, and such a good selector will soon be applied on a large scale.

last

This is the end of this article. I hope it will help you:)

If you want to get the most interesting CSS information, don’t miss my official account–ICSS front end anecdotes 😄

More wonderful CSS technical articles are summarized in myGithub — iCSS, continuous update, welcome to click star subscription collection.

If you have any questions or suggestions, you can communicate more. Original articles, limited writing style, shallow talent and learning, if there is anything wrong in the article, please let me know.