如何正确使用:has和:nth-last-child
我们可以用CSS检查,以了解一组元素的数量是否小于或等于一个数字。例如,一个拥有三个或更多子项的grid。你可能会想,为什么需要这样做呢?在某些情况下,一个组件或一个布局可能会根据子元素的数量而改变。
这在CSS中已经存在很多年了,但现在通过CSS :has,它变得更加强大。我们可以把nth-last-child选择器和:has结合起来,以达到神奇的效果!你没听错。
在这篇文章中,我将强调几个例子,说明我们可以将一个CSS选择器和:has结合起来,形成一个有条件的组件/布局状态。
总览
介绍
:nth-last-childCSS中的数量查询限制
- 不可能根据元素的数量来设计父元素的样式
- 让它们在不同的视口尺寸上奏效
- 为了控制间距要付出更多
使用案例
- 基于子项数量而变化的Grid
- 动态标题布局
- 动态新闻部分
- 模态框操作
- 用户头像
- 时间轴
- logo网格
总结
介绍:nth-last-child
这篇文章的主要要素之一是:nth-last-child伪类。我们可以使用该选择器来模拟计算子元素。
来看看它是如何工作的。我将尽可能用直白的话来解释。
请看下图:
我们有一个五个卡片的列表。我们将用这个例子来证明我们可以用:nth-last-child做什么。
在下列CSS中,n + 3意味着:
li:nth-last-child(n + 3) {
/* styles */
}
从末端选择前三项,从第三项开始计算。
让我们仔细看看。首先,我们需要从末端计算三个项。这样一来,第三项实际上就是我们从末端开始计算的第一项。
我们从第三项算起直到最后,这里是被选中的项:
CSS中的数量查询限制
我们可以使用:nth-last-child作为CSS的数量查询。
请看下图:
我们有一个信息清单,当我们有5个或更多的项时,它的显示方式会不同。
<ul>
<li></li>
<li></li>
<li></li>
<!-- more items -->
</ul>
li {
/* default styles */
}
/* If the list has 5 or more items */
li:nth-last-child(n + 5),
li:nth-last-child(n + 5) ~ li {
width: 50%;
display: inline-block;
border-bottom: 0;
}
虽然这很有效,但在某些方面仍然有点局限性。
不可能根据元素的数量来设计父元素的样式
想象一下,当有5个或更多的项时,我们需要为每个<li>添加display: flex。我们不能用 :nth-last-child 伪类选择器来做这个。
原因是,添加display: flex将迫使每个项留在自己的行中,这与要实现的设计不一致。
li:nth-last-child(n + 5),
li:nth-last-child(n + 5) ~ li {
width: 50%;
display: flex;
flex-direciton: column;
}
我们可以用display: inline-flex来解决这个问题,但对我来说,这仍然不是最佳解决方案。原因是,浏览器会考虑到HTML元素之间的间距,它们应该是这样的:
<ul>
<li></li><li></li><li></li>
<!-- more items -->
</ul>
如果我们不这样做,display: inline-flex的效果将与display: flex相同。解决这个问题的一个方法是将宽度减少1%。
li:nth-last-child(n + 5),
li:nth-last-child(n + 5) ~ li {
width: 49%;
display: flex;
flex-direciton: column;
}
让它们在不同的视口尺寸上奏效
如果没有对父类进行控制的能力,就不能那么直接地对列表的布局进行设计。例如,当容器或视口宽度较小时,我们需要每行显示1个项。
为了控制间距要付出更多
当有3个或更少的项时,间距是水平的,而当有5个或更多时,间距是垂直的。我们可以通过将页边距从水平方向翻转到垂直方向,或者通过使用CSS gap与Flexbox来手动管理。但是,在这种情况下,我们又不得不使用inline-flex。
CSS :nth-last-child伪类是构建条件性布局的关键。通过将它与CSS :has选择器相结合,我们可以检查一个父元素是否至少有特定数量的项,并对其进行相应的样式设计。这种可能性是无穷无尽的!
使用案例
基于子项数量而变化的Grid
当我们需要基于子项数量而更改gird布局时,这在目前的CSS中是不可能的。在CSS的grid中,我们可以使用minmax()基于可用空间来动态改变grid。
下面是我对CSS网格minmax()的看法:
.list {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 1rem;
}
结果看起来是这样:
这一点都不完美。我们没有太多的控制,因为我们需要调整minmax()中的150px的值。当有4个或更少的项时,它可以很好地工作,而当有5个或更多的项时就会出现问题。
解决办法是什么?我们可以用CSS :has检查是否有超过5个项目或更多,并在此基础上改变minmax()的值。
/* default grid */
.list {
--item-size: 200px;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(var(--item-size), 1fr));
gap: 1rem;
}
/* If the grid has 5+ items, change the --item-size width to 150px */
.list:has(li:nth-last-child(n + 5)) {
--item-size: 150px;
}
我只是改变了--item-size变量,使代码更容易阅读,并避免重复。
动态标题布局
在下图中,我们有一个标题,当导航项有4个或更多时,应该改变其布局。通过CSS :has和:nth-last-child,我们可以检测并改变布局。
.site-header:has(li:nth-last-child(n + 4)) {
.site-header__wrapper > * {
flex: initial;
}
.site-header__start {
order: 2;
}
.site-header__middle {
order: -1;
text-align: start;
}
.site-header__end {
margin-left: auto;
}
}
以上是Sass的代码。如果用CSS写,可能看起来有点多。
.site-header:has(li:nth-last-child(n + 4)) .site-header__wrapper > * {
flex: initial;
}
.site-header:has(li:nth-last-child(n + 4)) .site-header__start {
order: 2;
}
.site-header:has(li:nth-last-child(n + 4)) .site-header__middle {
order: -1;
text-align: start;
}
.site-header:has(li:nth-last-child(n + 4)) .site-header__end {
margin-left: auto;
}
我们能做得更好吗?可以。但这还没有得到很好的支持(目前来说)。我们可以添加一个布尔CSS变量,当标题有4个或更多的项目时,它将被切换,然后使用样式查询来改变标题。
.site-header:has(li:nth-last-child(n + 4)) {
--layout-2: true;
}
有了这个,当导航项有4个或更多时,我们设置变量--layout-2。
/* This will only works if the --layout-2 CSS variable is set */
@container style(--layout-2: true) {
.site-header__wrapper {
> * {
flex: initial;
}
}
.site-header__start {
order: 2;
}
.site-header__middle {
order: -1;
text-align: start;
}
.site-header__end {
margin-left: auto;
}
}
动态新闻部分
下面是一个新闻部分的设计,当项目数为3或更多时,它应该改变其布局。
通过组合CSS的:has和:nth-last-child,我们可以创建一个切换的CSS变量,它将被一个样式查询所检查。
首先,我将假设默认的卡片样式是水平的。
<div class="layout">
<article class="card"></article>
<article class="card"></article>
<article class="card"></article>
</div>
.layout {
display: grid;
grid-gap: 1rem;
}
.card {
display: flex;
gap: 1rem;
align-items: center;
}
然后,我需要检查.card元素的数量。
.layout:has(.card:nth-last-child(n + 4)) {
--layout-4: true;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}
现在,我们有一个CSS变量--layout-4,只有当我们有4个或更多的项时才会被切换。我们可以用一个样式查询来检查,并相应地更新.card的样式。
@container style(--layout-4: true) {
.card {
flex-direction: column;
}
.card__thumb {
flex: 1;
width: 100%;
aspect-ratio: 4 / 3;
}
}
模态框操作
在一个设计系统中,我们可能需要根据我们有多少个操作来动态地控制模态操作的排列。
请看下图:
比如说,如果只有一个操作,它应该居中。否则,向右对齐它们。
下面是CSS:
.modal__footer {
display: flex;
justify-content: center;
gap: 0.5rem;
}
/* If there are 2 buttons or more */
.modal__footer:has(a:nth-last-child(n + 2)) {
justify-content: flex-end;
}
很简单,对不对。
用户头像
在编辑网站上,一篇文章可能由多个作者撰写。一个常见的模式是,当我们有多个作者时,用负间距堆叠作者的图像。
仅仅通过使用数量查询,我们就可以最低限度的实现,也就是:
- 添加负间距(互相堆叠头像)。
- 当有多个头像时,缩小头像的尺寸。
img:nth-last-child(n+2) ~ img {
border: 2px solid #fff;
margin-left: -0.25rem;
width: 30px;
height: 30px;
}
上面的方法可行,但它有局限性。如果我们想对容器本身进行样式设计呢?那么,这就是CSS :has变得强大的地方。
首先,我们需要检查并切换CSS变量:
.post-author:has(img:nth-last-child(n + 2)) {
--multiple-avatars: true;
}
如果CSS变量为true,就为多个头像应用下面的样式:
@container style(--multiple-avatars: true) {
.avatars-list {
display: flex;
background-color: #efefef;
padding: 8px 12px;
border-radius: 50px;
}
img:not(:first-child) {
border: solid 2px #fff;
margin-left: -0.25rem;
}
}
时间线
另一个有趣的例子是时间线组件,它的CSS效果很好。
在这个例子中,我想让时间线在有4个或更多项时,从垂直列表切换到交替式。
首先,使用:nth-last-child和:has:
.timeline-wrapper:has(.timeline__item:nth-last-child(n + 4)) {
--alternating: true;
}
如果符合上述条件,将采用以下CSS:
@container style(--alternating: true) {
/* Alternating timeline styles. */
}
在这里使用样式查询的有用之处在于,我们可以在另一个页面上重复使用这些样式。它不一定非得是一个有条件的CSS。
我可能会做这样的事情:
.timeline-wrapper--page-10 {
--alternating: true;
}
请不要介意.timeline-wrapper--page-10,这是个故意的随机类名。这个CSS变量可以被分配到我们想要的任何地方,而且这个CSS开箱即用。
只要写一次,就能在很多情况下发挥作用。
logo网格
在CSS中,要处理的一个棘手问题是对齐多个标识,并确保它们都看起来不错。通过条件性CSS,我们可以检测logo的数量,并将其尺寸缩小一些。
ul:has(li:nth-last-child(n + 8)) img {
max-width: 160px;
height: 35px;
}
总结
这是我所做的有趣的文章之一。结合现代的CSS功能可以让我们以令人兴奋的新方式来构建布局,这篇文章的例子也不例外。
根据项目的数量来改变样式可能不是一次性的用法,它可以被提取到不同的用例中。通过使用样式查询,我们可以只写一次,并在任何地方重用它们。
以上就是本文的全部内容,如果对你有所帮助,欢迎点赞、收藏、转发~
如何正确使用:has和:nth-last-child的更多相关文章
- jQuery 2.0.3 源码分析Sizzle引擎 - 词法解析
声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 浏览器从下载文档到显示页面的过程是个复杂的过程,这里包含了重绘和重排.各家浏览器引擎的工作原理略有差别,但也有一定规则. 简 ...
- Python之路【第十八章】:Django基础
Django基本配置 Python的WEB框架有Django.Tornado.Flask 等多种,Django相较与其他WEB框架其优势为:大而全,框架本身集成了ORM.模型绑定.模板引擎.缓存.Se ...
- 分析Sizzle引擎 - 词法解析
分析Sizzle引擎 - 词法解析 声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 浏览器从下载文档到显示页面的过程是个复杂的过程,这里包含了重绘和重排.各家浏览器引擎的工 ...
- Tp5自动验证
<?php class DB { private $address = 'mysql.21future.com'; private $username = 'manbang'; private ...
- 一行代码轻松搞定各种IE兼容问题,IE6,IE7,IE8,IE9,IE10
在网站开发中不免因为各种兼容问题苦恼,针对兼容问题,其实IE给出了解决方案Google也给出了解决方案百度也应用了这种方案去解决IE的兼容问题? 百度源代码如下: <!Doctype html& ...
- Python开发【Django】:基础
Django基本配置 Python的WEB框架有Django.Tornado.Flask 等多种,Django相较与其他WEB框架其优势为:大而全,框架本身集成了ORM.模型绑定.模板引擎.缓存.Se ...
- FormsAuthentication 在asp.net MVC中的应用
说明:开发环境 vs2012 asp.net mvc4 c# 项目结构: 1.开发步骤 1.1 创建项目 打开vs2012 开发环境 “文件”--“新建”--“项目” 选择asp.net mvc项目类 ...
- 【原创】最有效解决IE8 position兼容性问题
看了网上的的帖子真是水的一塌糊涂,完全没有解决我和广大网友们的关于ie8下position兼容性问题. 网上有的技术我就不说了 ,大家自行搜索,我想说的重点是 ie8不支持html5的新标签.这是重点 ...
- 2016/4/5 Ajax ①用户名 密码 登陆 注册 ② 判断用户名是否已存在 ③点击按钮出现民族选项下拉菜单 ④DBDA类 加入Ajaxquery方法 数组变字符串 字符串拆分
①登陆 注册 查表匹配 0405Ajax.php ②判断用户名是否存在 <!DOCTYPE html> <html lang="en"> ...
- spring boot + spring security +前后端分离【跨域】配置 + ajax的json传输数据
1.前言 网上各个社区的博客参差不齐 ,给初学者很大的困扰 , 我琢磨了一天一夜,到各个社区找资料,然后不断测试,遇到各种坑,一言难尽啊,要么源码只有一部分,要么直接报错... 最后实在不行,直接去看 ...
随机推荐
- Websocket 60秒断开,连接不稳定
本地测试都是正常的,线上测试总是过一会就断开... 线上新增了https协议,导致页面中的链接必须也是ssl Websocket链接地址从ws://ws.xxx.com改成了wss://ws.xxx. ...
- java英文字符串首字母大写
java英文字符串首字母大写 /** * 首字母大写 * @param string * @return */ public static String toUpperCase4Index(Strin ...
- 民谣女神唱流行,基于AI人工智能so-vits库训练自己的音色模型(叶蓓/Python3.10)
流行天后孙燕姿的音色固然是极好的,但是目前全网都是她的声音复刻,听多了难免会有些审美疲劳,在网络上检索了一圈,还没有发现民谣歌手的音色模型,人就是这样,得不到的永远在骚动,本次我们自己构建训练集,来打 ...
- 2022-10-24:以下go语言代码输出什么?A:3 3;B:3 4;C:0 0;D:0 1。 package main func main() { m := make(map[int]int
2022-10-24:以下go语言代码输出什么?A:3 3:B:3 4:C:0 0:D:0 1. package main func main() { m := make(map[int]int, 3 ...
- 2022-06-10:薯队长从北向南穿过一片红薯地(南北长M,东西宽N),红薯地被划分为1x1的方格, 他可以从北边的任何一个格子出发,到达南边的任何一个格子, 但每一步只能走到东南、正南、西南方向的
2022-06-10:薯队长从北向南穿过一片红薯地(南北长M,东西宽N),红薯地被划分为1x1的方格, 他可以从北边的任何一个格子出发,到达南边的任何一个格子, 但每一步只能走到东南.正南.西南方向的 ...
- 2022-02-28:k8s安装adminer,yaml如何写?
2022-02-28:k8s安装adminer,yaml如何写? 答案2022-02-28: yaml如下: apiVersion: apps/v1 kind: Deployment metadata ...
- 2021-04-29:给定一个数组 arr,代表一排有分数的气球。每打爆一个气球都能获得分数,假设打爆气 球 的分数为 X,获得分数的规则如下: 1)如果被打爆气球的左边有没被打爆的气球,找到离被打爆
2021-04-29:给定一个数组 arr,代表一排有分数的气球.每打爆一个气球都能获得分数,假设打爆气 球 的分数为 X,获得分数的规则如下: 1)如果被打爆气球的左边有没被打爆的气球,找到离被打爆 ...
- 2021-11-29:给定一个单链表的头节点head,每个节点都有value(>0),给定一个正数m, value%m的值一样的节点算一类, 请把所有的类根据单链表的方式重新连接好,返回每一类的头节点
2021-11-29:给定一个单链表的头节点head,每个节点都有value(>0),给定一个正数m, value%m的值一样的节点算一类, 请把所有的类根据单链表的方式重新连接好,返回每一类的 ...
- 安装vue后 vue -V查不到版本号 添加环境变量没反应
萌新一枚,今天听老大说后续有项目要用vue,于是找教程自己先看看, 验证vue是否安装成功时,提示 'vue' 不是内部命令,也不是可运行的程序 有说把vue路径添加环境变量的,试了一下,不行(后来看 ...
- 数据库SQL复习
数据库SQL介绍 Def:SQL是一种极其高效的数据库系统语言:可以实现对数据库中的数据进行增删改查等操作 增加操作:使用create命令: 可以create table 可以create View ...