关于css:CSS-有了has伪类可以做些什么

45次阅读

共计 5033 个字符,预计需要花费 13 分钟才能阅读完成。

置信大家最近对 :has 都有所耳闻,标准提及了那么久,却迟迟没有动静,最近浏览器终于开始反对了~🎉🎉

:has伪类是一个十分弱小的伪类,弱小到难以置信,能够做很多梦寐以求的事件,很多以前只能更改 dom 构造 或者只能用 JS 能力实现的性能当初也能纯 CSS 实现了,一起看看吧

一、简略介绍一下:has

:has伪类的语法非常简单,示意满足肯定条件后,就会匹配该元素。

例如,上面的选择器只会匹配间接蕴含 img 子元素的 a 元素:

a:has(> img)

再例如,上面的选择器只会匹配其后紧跟着 p 元素的 h1 元素

h1:has(+ p)

以我集体的了解来看,去除 :has() 后,剩下的选择器依然是残缺的

a>img

加上 :has() 后,能够选中最后面的元素a

好了,语法其实就这么多,预计没啥吸引力,要害是理论利用。上面通过几个实例来感受一下 :has 伪类的弱小魅力~

舒适揭示:兼容性要求须要 Chrome 101+,并且开始试验个性(105+ 正式反对),Safari 15.4+,Firefox 官网说开启试验个性能够反对,然而实测并没有(???)

二、表单元素必填项

先来看一个简略例子,上面有一个表单元素,有一些是必填项

<form>
  <item>
    <label> 用户名 </label>
    <input required>
  </item>
  <item>
    <label> 备注 </label>
    <input>
  </item>
</form>

当初能够通过 :has 在必填项的后面加上红色的星号

label:has(+input:required)::before{
  content: '*';
  color: red;
}

这个应该还比拟好了解,通过 :has+能够选中满足条件的 label,而后再生成::before 伪元素。如果是在以前,可能须要手动增加类名,或者扭转 html 的书写程序

你也能够拜访线上残缺 demo:has+required(runjs.work)

三、拖拽指定区域

有些时候列表须要有拖拽性能,但为了拖拽体验,不影响列表外部操作,可能须要指定小局部区域可拖拽,例如

HTML 构造如下

<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>

当初咱们心愿在 hover 时呈现拖拽手柄,按住拖拽手柄才能够拖拽,看着如同十分麻烦,然而当初借助 :has 伪类能够轻易实现,要害 CSS 如下

.thumb{
  /**/
  opacity: 0
}
.item:hover .thumb{opacity: 1;}
.item:has(.thumb:hover){-webkit-user-drag: element;}

这里的 :has 示意当 .thumb 处于 :hover 状态时选中该元素,从而给 .item 增加可拖拽属性,成果如下

你也能够拜访线上残缺 demo:drag_thumb(runjs.work)

四、多层级 hover

再来看一个例子,早在四年前我就提到过的 CSS 多层级 hover 问题,当初终于有解了~

是这样的,有一个多层级的构造,例如

<div class="box-1">
  <div class="box-2">
    <div class="box-3"></div>
  </div>
</div>

如果给 div 增加 hover 款式

div:hover{outline:4px dashed rebeccapurple}

成果是这样

能够看到,当 hover 到里层元素时,外层元素也触发了 hover 款式。这有点像 JS 中的冒泡成果,那如何让 hover 的时候只触发以后的元素呢?也就是排除掉他的父级元素,没错,:has能够很好的解决这个问题

div:not(:has(:hover)):hover{outline:4px dashed rebeccapurple}

是不是越来越绕了?别急,咱们拆解剖析一下,div:has(:hover)示意有子元素正处于 hoverdiv,比方当 hoverbox-3时,div:has(:hover)选中的就是除 box-3 以外的两个父级,而后加上 :not 就刚好反过来,只选中 box-3 自身,能够了解吗?这个能够下来多试试,实际效果如下

你也能够拜访线上残缺 demo:CSS hover(runjs.work)

在一些可视化拖拽平台,各种嵌套的组件中会很有作用

五、评星组件

这个性能也是非常适合用 :has 来实现的,HTML 构造如下

<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>

简略润饰一下

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;
}

成果如下

上面要做交互性能,当 :hover 或者 :checked 时,以后元素和以后元素之前的元素都触发选中。

在之前,因为只有后置兄弟选择器~,所以必须要将 dom 元素更改程序,而后通过其余形式在视觉上再翻转过去。当初有了:has,这些奇技淫巧都能够说拜拜了,实现如下

star [type="radio"]:hover,
star [type="radio"]:has(~:hover),
star:not(:hover) [type="radio"]:checked,
star:not(:hover) [type="radio"]:has(~:checked){background: orangered;}

置信不算太简单,[type="radio"]:has(~:hover)示意选中以后 hover 元素之前的元素,所以能够轻易的实现评分的成果

你也能够拜访线上残缺 demo:CSS rate(runjs.work)

六、日期范畴抉择

如果说下面这些例子有其余代替计划,或者说用一点点 JS 也能实现,那上面来一个重磅级的案例。在以前,就算靠 JS 也会有一些麻烦,然而有了:has,所有都变得简略了~

假如 HTML 构造如下

<div class="date">
  <span>1</span>
  <span>2</span>
  <span>3</span>
  ...
  <span>30</span>
  <span>31</span>
</div>

这部分交互有两个局部,一个是鼠标滑过,还有一个是选中。

咱们先看选中的性能,当有两个元素被选中时,这两个元素之间的元素都会匹配上,假如选中的类名是select

<div class="date">
  <span>1</span>
  <span>2</span>
  <span class="select">3</span>
  ...
  <span class="select">30</span>
  <span>31</span>
</div>

那么,如何让这一片区域的元素都匹配上呢?答案就是通过 :has 找到 select 之前的元素,再联合 ~ 匹配之后的元素,两者联合就能够匹配到两头的元素了,具体实现如下

.select,
.select~span:has(~.select){
   background-color: blueviolet;
   color: #fff;
}

成果如下

而后是 hover 的成果,假如有一个曾经被选中了

<div class="date">
  <span>1</span>
  <span>2</span>
  <span class="select">3</span>
  ...
  <span>30</span>
  <span>31</span>
</div>

当初须要再鼠标滑过的时候,将鼠标的起点和已选中的范畴都匹配上,这个就略微有些简单了,咱们须要思考鼠标在已选中元素之前还是之后,别离用 :has 进行判断,实现如下

span:hover~span:has(~.select),
.select~span:has(~:hover)
{
  background-color: blueviolet;
  color: #fff;
}

是不是有些晕了?第一条示意鼠标在已选中之前,匹配以后 hover 之后、.selelct之前的元素,第二条示意已选中之后,匹配 .selelct 之后、hover之前的元素,实际效果如下

还有一个问题,须要辨别一下选中两个和只选中一个的状况,因为两个示意区间抉择曾经实现,此时 hover 不会有成果,借助 :has 伪类,能够很轻易的区分子元素的个数,如下

.date:not(:has(.select~.select)){/* 匹配到没有两个.select 的父级 */}

.select~.select示意选中 .select 前面的 .select,也就是示意至多有两个.select,而后通过:has 就能够辨别这两种状况了

.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;
}

元素的选中时通过 JS 实现的,这时候的 JS 齐全就只是工具人了,和视觉齐全不相干,只须要记录选中的元素,逻辑极其简略,如下

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')
})

而后就能够失去这样的成果了

你也能够拜访线上残缺 demo:CSS date-range(runjs.work)

期待时机成熟,xy-ui 中的 date-picker 也会跟进应用这一计划~

七、别等了,当初就学起来

以上就是我在钻研 :has 伪类后,第一工夫所想到的一些利用场景,这么好用的伪类,还不学一下吗?

当然其作用远不止这些,能够这么讲,以前须要思考 dom 程序的场景都能够解决了,当前的 dom 将更加语义化,大部分交互状态伪类都能够很好地联合在一起,80% 以上交互相干的 JS 代码都能够去掉了,JS 能够更加安心的去做本人该做的事件了,比方数据转换,业务逻辑等等。

再等一年,公司的一些外部我的项目就能够大胆用起来了,嘿嘿嘿,我曾经又有新的想法了,下次分享。最初,如果感觉还不错,对你有帮忙的话,欢送点赞、珍藏、转发❤❤❤

正文完
 0