根据元素数量匹配样式,解决 向前选择 和 重复元素 问题。
<封面摄于江苏·南京的先锋书店,打卡网红书店。>
举个栗子
场景一
今天在给 hexo-theme-zhaoo
写 文章目录 (toc),有个需求:当目录不存在时,不显示标题。
文章目录
抽象出 DOM
结构:
<aside class="toc-wrap"> <h3 class="toc-title">文章目录:</h3> <ol class="toc">...</ol> </aside>
|
最直接的思路是匹配 .toc
,若该元素不存在,则将它前面的 .toc-title
隐藏。但是 CSS
没有向前选择,因为这样回引起回流。
需要想个办法曲线救国,其实借助它们的公共父元素 .toc-wrap
即可解决,若 .toc-wrap
只有一个子元素则将它隐藏。如下:
.toc-wrap & > :only-child display none
|
🌰 很简单,却值得思考一下,CSS
真的不能 “向前选择” 嘛?
场景二
在构建 栅格化 布局的时候,早期的 BootStrap
使用了大量的重复代码,类似这样:
@media (min-width: 768px) { .col-md-1 { flex: 0 0 8.33333%; max-width: 8.33333%; } .col-md-2 { flex: 0 0 16.6667%; max-width: 16.6667%; } .col-md-3 { flex: 0 0 25%; max-width: 25%; } .col-md-4 { flex: 0 0 33.3333%; max-width: 33.3333%; } .col-md-5 { flex: 0 0 41.6667%; max-width: 41.6667%; } .col-md-6 { flex: 0 0 50%; max-width: 50%; } .col-md-7 { flex: 0 0 58.3333%; max-width: 58.3333%; } .col-md-8 { flex: 0 0 66.6667%; max-width: 66.6667%; } .col-md-9 { flex: 0 0 75%; max-width: 75%; } .col-md-10 { flex: 0 0 83.3333%; max-width: 83.3333%; } .col-md-11 { flex: 0 0 91.6667%; max-width: 91.6667%; } .col-md-12 { flex: 0 0 100%; max-width: 100%; } } @media (min-width: 992px) { .col-lg-1 { flex: 0 0 8.33333%; max-width: 8.33333%; } .col-lg-2 { flex: 0 0 16.6667%; max-width: 16.6667%; } .col-lg-3 { flex: 0 0 25%; max-width: 25%; } .col-lg-4 { flex: 0 0 33.3333%; max-width: 33.3333%; } .col-lg-5 { flex: 0 0 41.6667%; max-width: 41.6667%; } .col-lg-6 { flex: 0 0 50%; max-width: 50%; } .col-lg-7 { flex: 0 0 58.3333%; max-width: 58.3333%; } .col-lg-8 { flex: 0 0 66.6667%; max-width: 66.6667%; } .col-lg-9 { flex: 0 0 75%; max-width: 75%; } .col-lg-10 { flex: 0 0 83.3333%; max-width: 83.3333%; } .col-lg-11 { flex: 0 0 91.6667%; max-width: 91.6667%; } .col-lg-12 { flex: 0 0 100%; max-width: 100%; } }
|
在使用 CSS 预处理器
后可以简化成下面这样,但是编译后依然存在大量重复代码。
for $i in 1 .. 12 .col-{$i} col-attr round(($i * 100 / 12) %, 6) @media (min-width 576px) for $i in 1 .. 12 .col-sm-{$i} col-attr round(($i * 100 / 12) %, 6) @media (min-width 768px) for $i in 1 .. 12 .col-md-{$i} col-attr round(($i * 100 / 12) %, 6) @media (min-width 992px) for $i in 1 .. 12 .col-lg-{$i} col-attr round(($i * 100 / 12) %, 6) @media (min-width 1200px) for $i in 1 .. 12 .col-xl-{$i} col-attr round(($i * 100 / 12) %, 6)
|
灵魂拷问,CSS
真的不适合处理 “重复元素” 的场景嘛?
前置知识
一些 CSS3
选择器:
>
子元素选择器
+
相邻元素选择器
~
兄弟元素选择器
一些 CSS3
伪类:
:only-child
父元素的唯一子元素
:first-child
父元素的第一个子元素
:last-child
父元素的最后一个子元素
:nth-child(n)
父元素的第 N 个子元素
:nth-last-child(n)
父元素的倒数第 N 个子元素
:nth-child(xn+y)
父元素的第 xN + y 个子元素
:nth-last-child(xn+y)
父元素的倒数第 xN + y 个子元素
化学反应
结合以上的 选择器 和 伪类 可以产生一些有趣的 “化学反应”,基于元素的数量来匹配样式。
DOM
结构如下:
<ul> <li>1</li> <li>2</li> <li>3</li> ... </ul>
|
André Luís
André Luís
方案如下:
ul>li:nth-child(1):nth-last-child(1) { width: 100%; } ul>li:only-child { width: 100%; }
|
ul>li:nth-child(1):nth-last-child(2), ul>li:nth-child(2):nth-last-child(1) { width: 50%; }
|
ul>li:nth-child(1):nth-last-child(3), ul>li:nth-child(2):nth-last-child(2), ul>li:nth-child(3):nth-last-child(1) { width: 33.3333%; }
|
Clever lists with CSS3 selectors
Lea Verou
上述方案存在一个问题,当元素数量过多时,还是存在大量的重复选择器。升级版的 Lea Verou
方案只需两行固定的选择器即可解决。
ul>li:first-child:nth-last-child(1) { width: 100%; }
|
ul>li:first-child:nth-last-child(2), ul>li:first-child:nth-last-child(2) ~ li { width: 50%; }
|
ul>li:first-child:nth-last-child(3), ul>li:first-child:nth-last-child(3) ~ li { width: 33.3333%; }
|
Styling elements based on sibling count
再改进
不确定元素数量时:
ul>li:first-child:nth-last-child(n+3), ul>li:first-child:nth-last-child(n+3) ~ li { width: calc(100% / n); }
|
总结
通过上述方法即可解决 向前选择 和 重复元素 这两个问题,遇到实际场景时借助父元素灵活运用即可。