你可能之前使用过:nth-伪元素选择器:比如使用:last-child从列表中去掉最后一个元素的边框;或者使用:first-child为一篇文章开始的段落添加一个边框,如下所示。

p:first-child { 
padding-bottom : 1.5rem; 
border-bottom : 1px solid #ebf4f6; 
font-size : 1rem; }

还不错,只是选择首尾的元素来做一些简单的样式定义。只能做到这些吗?当然不是。

在处理可预见的情况时(列表中的项或者表格中的行),:nth-child选择器的表现不错。但当我们不能预知元素的位置时,就需要一个更加灵活的选择。如果能根据类型或者元素在文档中的位置来选择岂不是更好?其实这就是:nth-of-type伪元素选择器要做的,也是CSS的秘密之一。

想选中第一段,不管它出现在文档中的什么位置,都不是问题。那么要选中第四个无序列表例子中的第十三项呢?:nth-of-type同样可以帮助你。任何目标元素,无论它出现在什么位置,都不需要id或者class属性,这确实很强大。

:nth-of-type参数

:nth-of-type可以接受像odd或者even这样的关键字,也可以是数字或者表达式。听起来有点复杂,其实并不是,我们一起看几个例子。

如果你想为列表里的奇数项(一、三、五、七…)添加边框,使用:nth-of-type就可以很轻松实现。你不需要为HTML添加class属性或者使用JavaScript hack,只需要用odd关键字就可以。

li:nth-of-type(odd) { 
border-bottom : 1px solid #ebf4f6; }

在下面的例子中,使用:nth-of-type选择器给文章中的第一段文字加粗。

article p:nth-of-type(1) { 
font-weight : bold; }

表达式相对复杂些,刚接触时我们都会觉得很烧脑。我的建议是从右往左倒着阅读。在下面的例子中,3n+1表示匹配表格里的第一行(1),然后是每隔3行(3n)。

tr:nth-of-type(3n+1) { 
background-color : #fff; }

6n+3将匹配每隔六个元素后的第三个元素。

tr:nth-of-type(6n+3) { 
opacity : .8; }

现在使用:nth-of-type伪元素选择器来为每张卡片添加背景图像。

.h-card:nth-of-type(1) { 
background-image : url(card-01.jpg); } 

.h-card:nth-of-type(2) { 
background-image : url(card-02.jpg); } 

.h-card:nth-of-type(3) { 
background-image : url(card-03.jpg); } 

.h-card:nth-of-type(4) { 
background-image : url(card-04.jpg); } 

.h-card:nth-of-type(5) { 
background-image : url(card-05.jpg); } 

.h-card:nth-of-type(6) { 
background-image : url(card-06.jpg); } 

.h-card:nth-of-type(7) { 
background-image : url(card-07.jpg); } 

.h-card:nth-of-type(8) { 
background-image : url(card-08.jpg); } 

.h-card:nth-of-type(9) { 
background-image : url(card-10.jpg)); }

1810.tif

在小屏幕上,卡片整齐地摆在一起。

因为我们只是希望显示元素的背景图像,所以通过缩进把HTML文本移到屏幕外。

.h-card * { 
text-indent : -9999px; }

那些卡片虽然看起来不错,但是有点呆板,接下来我们给它们添加一些旋转变换的效果。我们不会给特定的卡片应用这些转换,而是通过:nth-of-type(n)选择器来让设计看起来是随机的。我们把奇数的卡片逆时针旋转2度(-2deg),让它们显得松散一些。

.h-card:nth-child(odd) { 
transform : rotate(-2deg); 
transform-origin : 0 100%; }

现在让我们继续调整,给3、4、6的倍数的每一个卡片设置不同的rotate值,给6的倍数的卡片设置translate值,让它们偏离原点。

.h-card:nth-child(3n) { 
transform : rotate(2deg) translateY(-30px); } 

.h-card:nth-child(4n) { 
transform : rotate(2deg); 
transform-origin : 0 100%; } 

.h-card:nth-child(6n) { 
transform : rotate(-5deg); 
transform-origin : 0 0; }

1811.tif

多亏了transform和伪元素选择器,现在这堆卡片变得一团糟。

在小屏幕上,那些名片很适合垂直的布局,然而在中大型屏幕上,垂直布局并不能很好地利用可用的空间。所以我们接下来使用伪元素通过定位和一些transform,将卡片排成一个网格的形状。

再回到设计本身,我们需要给每张卡片设置定位,但又不需要在小屏幕上生效,所以我们使用媒体查询来让这些样式只应用于中大型屏幕。

@media (min-width: 48rem) { 
.h-card { 
position : absolute; } 
}

通过定位,给每张卡片的顶部top和左侧left设置一些值,用来形成一个松散的网格。

@media (min-width: 48rem) { 
.h-card:nth-of-type(1) { 
top : 100px; 
left : 0; } 

.h-card:nth-of-type(2) { 
top : 80px; 
left : 320px; } 

.h-card:nth-of-type(3) { 
top : 100px; 
left : 640px; } 

.h-card:nth-of-type(4) { 
top : 320px; 
left : 40px; } 
.h-card:nth-of-type(5) { 
top : 270px; 
left : 570px; } 

.h-card:nth-of-type(6) { 
top : 320px; 
left : 600px; } 

.h-card:nth-of-type(7) { 
top : 540px; 
left : 0; } 

.h-card:nth-of-type(8) { 
top : 560px; 
left : 320px; } 

.h-card:nth-of-type(9) { 
top : 540px; 
left : 640px; } 
}

1812.tif

通过应用rotatetranslate,设计看起来更自然。

你应该已经发现我故意出的错。第五张卡片与水平放置的卡片不同,它是垂直的。把卡片顺时针旋转90deg,这个问题就解决了。旋转的原点transform-origin位于卡片的左上角。

@media (min-width: 48rem) { 
.h-card:nth-of-type(5) { 
transform : rotate(90deg); 
transform-origin : 0 0; } 
}

1813.tif

当我们把它顺时针旋转90deg并与其他卡片重叠后,这个孤零零的卡片看起来就好多了。

最后我们再做一点润色,给这些卡片添加一些RGBa阴影。

.h-card { 
box-shadow : 
0 2px 1px rgba(0,0,0,.8), 
0 2px 10px rgba(0,0,0,.5); }

1814.tif

放大设计,左侧柔和的RGBa阴影增添了景深。