CSS's :has()
pseudo-class is undoubtedly one of my favorite new features of CSS, and I believe many participants in the State of CSS survey agree with this. This ability to "invert" selectors gives us unexpectedly powerful capabilities.
The reason why it is called "more powerful" is because many smart developers have published many amazing clever ideas, such as:
This article is not an authoritative guide for :has()
, nor is it a duplication of existing content. I just want to share some of the ways I most likely use :has()
in my daily work…of course, the premise is that the browser support is good enough. (Firefox is the last browser to persevere, but it will be supported soon.)
Once the browser support is perfect, I will definitely use it everywhere :has()
. Here are some real cases I have built recently, and I thought to myself, “How simple will this be once :has()
is fully supported!”
Have you ever built an interactive component that sometimes needs to influence the style of the page elsewhere? In the following example, <nav></nav>
is a mega menu, opening it changes the color of the content above.
I feel like I always need to do this kind of thing.
This specific example is the React component I made for a website. I had to use document.querySelector(...)
outside the React section of the "touch" page and toggle a class on <header></header>
, <main></main>
, or other components. This is not the end of the world, but it does feel a little awkward. Even in a complete React website (such as Next.js website), I had to choose between managing the menuIsOpen
state on a higher component tree or performing the same DOM element selection - which is not quite React-style. With :has()
, the problem is solved:
header:has(.megamenu--open) { /* 如果包含具有类“.megamenu--open”的元素,则以不同的方式设置 header 样式 */ }
My JavaScript component no longer needs to deal with other parts of the DOM!
Adding alternating row "stripes" to a table can improve the user experience. They help your eyes track the rows you are currently in when scanning the table.
But in my experience, this doesn't work well on tables with only two or three rows. For example, if your table has three rows in <tbody> and you "stripe" each "even" row, you may end up with only one stripe. This is not worthy of this pattern and may make the user wonder what is special about that highlighted line. Using Bramus Using <code>:has()
The technique of applying styles based on the number of child elements, we can apply table stripes when there are more than three rows:
Want more advanced features? You can also decide to do this only if the table has at least a specific number of columns:
header:has(.megamenu--open) { /* 如果包含具有类“.megamenu--open”的元素,则以不同的方式设置 header 样式 */ }
I often need to change the page layout based on what is on the page. Taking the following grid layout as an example, the location of the main content will change the grid area according to whether the sidebar exists.
This may depend on whether the sibling page is set in the CMS. I usually use template logic to conditionally add BEM modifier classes to the layout wrapper to fit both layouts. The CSS may look like this (responsive rules and other content are omitted for brevity):
table:has(:is(td, th):nth-child(3)) { /* 只有在有三列或更多列时才执行操作 */ }
Of course, from a CSS perspective, this is absolutely fine. But this does make the template code a little messy. Depending on your template language, adding a bunch of classes conditionally can become very ugly, especially if you have to do the same with many child elements.
Compare this with a method based on :has()
:
/* m = 主要内容 */ /* s = 侧边栏 */ .standard-page--with-sidebar { grid-template-areas: 's s s m m m m m m m m m'; } .standard-page--without-sidebar { grid-template-areas: '. m m m m m m m m m . .'; }
Honestly, from a CSS perspective, this is not much better. But in my opinion, removing the conditional modifier class from the HTML template is a good gain.
It's easy to think of the micro design decisions of :has()
- when images are included in the card, for example - but I think it's also very useful for these macro layout changes.
If you read my previous articles, you will know that I am a persistent in specificity. If you are like me and don't want specific scores to surge when adding :has()
and :not()
to the entire style, be sure to use :where()
.
This is because the specificity of :has()
is based on the most specific element of in its parameter list. So if you include something like ID in it (I don't know why!) your selector will be hard to overwrite in the cascade.
is always zero and will never increase the specificity score. :where()
/* m = 主要内容 */ /* s = 侧边栏 */ .standard-page:has(.sidebar) { grid-template-areas: 's s s m m m m m m m m m'; } .standard-page:not(:has(.sidebar)) { grid-template-areas: '. m m m m m m m m m . .'; }
? What real cases have you encountered that would be the perfect solution? :has()
The above is the detailed content of More Real-World Uses for :has(). For more information, please follow other related articles on the PHP Chinese website!