I believe that everyone has heard about :has
recently. The specification has been mentioned for so long, but there has been no movement. Recently, the browser has finally started to support it~🎉🎉
:has
Pseudo-class is a very powerful pseudo-class, which is unbelievably powerful and can do a lot of dream things. Many functions that used to only change the DOM structure or can only be achieved with JS can now be made with pure CSS It's done, let's see it together
1. A brief introduction :has
:has
The syntax of the pseudo-class is very simple, which means that the element will be matched when certain conditions are met.
For example, the following selector will only match ---b479b4075d144dc8c523fbc5f016f897--- elements that directly contain the --- a
img
child element:
a:has(> img)
For another example, the following selector will only match the p
h1
element
h1:has(+ p)
From my personal understanding, after removing :has()
, the remaining selectors are still intact
a>img
After adding :has()
, the first element a
can be selected.
Well, the grammar is actually so much, it is estimated that it is not attractive, the key is practical application. Let's take a few examples to feel the powerful charm of pseudo-classes :has
Reminder: Chrome 101+ is required for compatibility, and experimental features are started (105+ is officially supported), Safari 15.4+, Firefox officially says that the experimental features can be supported, but the actual measurement does not (???)
2. Form elements are required
Let's first look at a simple example, there is a form element below, some of which are required
<form>
<item>
<label>用户名</label>
<input required>
</item>
<item>
<label>备注</label>
<input>
</item>
</form>
This is now available via :has
with a red asterisk in front of the required fields
label:has(+input:required)::before{
content: '*';
color: red;
}
这个应该还比较好理解, :has
+
label
,然后::before
伪元素。 If it was before, you may need to manually add the class name, or change the writing order of html
You can also visit the full demo online: has+required(runjs.work)
3. Drag and drop the designated area
Sometimes the list needs to have the drag and drop function, but in order to drag and drop the experience without affecting the internal operation of the list, it may be necessary to specify a small area to be dragged, for example
The HTML structure is as follows
<div class="content">
<div class="item">列表<span class="thumb"></span></div>
<div class="item">列表<span class="thumb"></span></div>
<div class="item">列表<span class="thumb"></span></div>
</div>
Now we hope that the drag handle will appear when hover
, and you can drag and drop by pressing and holding the drag handle. It seems very troublesome, but now with the help of :has
pseudo-class can be easily implemented, the key CSS is as follows
.thumb{
/**/
opacity: 0
}
.item:hover .thumb{
opacity: 1;
}
.item:has(.thumb:hover){
-webkit-user-drag: element;
}
Here :has
means that when .thumb
is in the :hover
state, the element can be selected and dragged, so as to add the draggable attribute to .item
as follows
You can also visit the full demo online: drag_thumb(runjs.work)
4. Multi-level hover
Let's look at another example. The CSS multi-level hover problem I mentioned four years ago is finally solved~
It is like this, there is a multi-level structure such as
<div class="box-1">
<div class="box-2">
<div class="box-3"></div>
</div>
</div>
If you add hover
style to div
491e6a589c82f0d2de4c08801df8e814---
div:hover{
outline:4px dashed rebeccapurple
}
The effect is this
It can be seen that when hover
goes to the inner element, the outer element also triggers the hover
style. This is a bit like the bubbling effect in JS
4b1fd96308125e76a2bbd897cbf90c8b---, so how to make hover
only trigger the current element? That is to exclude his parent element, yes, :has
can solve this problem very well
div:not(:has(:hover)):hover{
outline:4px dashed rebeccapurple
}
Is it getting more and more round?别急,我们拆解分析一下, div:has(:hover)
表示有子元素正hover
的div
,比如当hover
到box-3
时, div:has(:hover)
就是除box-3
父级, :not
,只选中box-3
itself, understandable? This can be tried more, the actual effect is as follows
You can also visit the full demo online: CSS hover(runjs.work)
In some visual drag and drop platforms, it will be very useful in various nested components
5. Star rating components
This function is also very suitable to use :has
to achieve, the HTML structure is as follows
<star>
<input name="star" type="radio">
<input name="star" type="radio">
<input name="star" type="radio">
<input name="star" type="radio">
<input name="star" type="radio">
</star>
Just make up
star{
display: flex;
}
star [type="radio"]{
appearance: none;
width: 40px;
height: 40px;
margin: 0;
cursor: pointer;
background: #ccc;
transition: .3s;
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E %3Cpath d='M462.3 62.6C407.5 15.9 326 24.3 275.7 76.2L256 96.5l-19.7-20.3C186.1 24.3 104.5 15.9 49.7 62.6c-62.8 53.6-66.1 149.8-9.9 207.9l193.5 199.8c12.5 12.9 32.8 12.9 45.3 0l193.5-199.8c56.3-58.1 53-154.3-9.8-207.9z'%3E%3C/path%3E %3C/svg%3E") center / 80% no-repeat;
}
The effect is as follows
The following is an interactive function. When :hover
or :checked
, both the current element and the element before the current element are triggered to be selected.
Previously, since there was only a post-sibling selector ~
, the dom elements had to be reordered and then visually flipped by other means. Now that we have :has
, we can say goodbye to these tricks and tricks. The implementation is as follows
star [type="radio"]:hover,
star [type="radio"]:has(~:hover),
star:not(:hover) [type="radio"]:checked,
star:not(:hover) [type="radio"]:has(~:checked){
background: orangered;
}
I believe it is not too complicated, [type="radio"]:has(~:hover)
means that the element before the current hover
element is selected, so the effect of scoring can be easily achieved
You can also visit the full demo online: CSS rate(runjs.work)
6. Date range selection
If there are other alternatives to the above examples, or can be achieved with a little JS, then here is a heavyweight case. In the past, even relying on JS would have some troubles, but with :has
, everything becomes easier~
Suppose the HTML structure is as follows
<div class="date">
<span>1</span>
<span>2</span>
<span>3</span>
...
<span>30</span>
<span>31</span>
</div>
This part of the interaction has two parts, one is the mouse over, and the other is the selection.
Let's look at the selected function first. When two elements are selected, the elements between the two elements will be matched. Suppose the selected class name is select
<div class="date">
<span>1</span>
<span>2</span>
<span class="select">3</span>
...
<span class="select">30</span>
<span>31</span>
</div>
So, how to make the elements in this area match? The answer is to find the elements before select
2bd16149f5f945fd71cf52049323a7a3--- through :has
, and then combine the elements after matching with ~
, the combination of the two can match the elements in the middle. as follows
.select,
.select~span:has(~.select){
background-color: blueviolet;
color: #fff;
}
The effect is as follows
Then there is the effect of hover
, assuming one has been selected
<div class="date">
<span>1</span>
<span>2</span>
<span class="select">3</span>
...
<span>30</span>
<span>31</span>
</div>
Now when the mouse needs to slide over, match the end point of the mouse with the selected range. This is a little more complicated. We need to consider whether the mouse is before or after the selected element, and use :has
Make judgments as follows
span:hover~span:has(~.select),
.select~span:has(~:hover)
{
background-color: blueviolet;
color: #fff;
}
Are you a little dizzy? The first line indicates that the mouse is selected before it matches the current hover
after .selelct
and before .selelct
. hover
The previous element, the actual effect is as follows
There is another problem. It is necessary to distinguish between two selected and only one selected, because two indicate that the interval selection has been completed. At this time, hover
will not have an effect. With the help of :has
pseudo class, which can easily distinguish the number of sub-elements, as follows
.date:not(:has(.select~.select)){
/*匹配到没有两个.select的父级*/
}
.select~.select
.select
.select
,也就是.select
, :has
It is possible to distinguish between the two
.date:not(:has(.select~.select)) .select,
.date:not(:has(.select~.select)) span:hover{
background-color: transparent;
color: inherit;
outline: 2px solid blueviolet;
outline-offset: -2px;
}
.date:not(:has(.select~.select)) span:hover~span:has(~.select),
.date:not(:has(.select~.select)) .select~span:has(~:hover)
{
background-color: blueviolet;
color: #fff;
}
The selection of elements is achieved through JS
, at this time JS
is completely just a tool person, completely irrelevant to vision, only need to record the selected elements, the logic is extremely simple, as follows
date.addEventListener('click', ev => {
const current = date.querySelectorAll('.select');
if (current.length == 2) {
current.forEach(el => {
el.classList.remove('select')
})
}
ev.target.classList.add('select')
})
Then you can get this effect
You can also visit the full demo online: CSS date-range(runjs.work)
When the time is right, the date-picker in xy-ui will follow up to use this scheme~
7. Don't wait, learn now
The above are some of the application scenarios that came to my mind at the first time after researching the :has
pseudo-classes, such a useful pseudo-class, why don't you learn it?
Of course, its role is much more than that. It can be said that the scenarios that needed to consider the order of the dom in the past can be solved. In the future, the dom will be more semantic. Most of the interactive state pseudo-classes can be well combined, and more than 80% of the interaction The related JS code can be removed, and JS can do what it needs to do with more peace of mind, such as data conversion, business logic, and so on.
After another year, some internal projects of the company can be used boldly. Hey, I already have new ideas. I will share them next time. Finally, if you think it's good and helpful to you, please like, bookmark, and forward ❤❤❤
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。