CSS秘密花园: 相邻元素样式
《CSS Secrets》是@Lea Verou最新著作,这本书讲解了有关于CSS中一些小秘密。是一本CSSer值得一读的一本书,经过一段时间的阅读,我、@南北和@彦子一起将在W3cplus发布一系列相关的读后感,与大家一起分享。
问题
在很多情况下,我们需要给元素的兄弟元素以不同的样式风格呈现。主要的用例是提高用户体验和在大屏幕中不断增加列表荐。下面是一些使用场景:
邮件列表或类似基于文的列表展示。如果我们只有为数不多的列表项,我们可以单独展示。随着列表项的增加,我们可以减少列表的预览项。当列表的高度大于视窗高时,我们可能会选择隐藏一些列表项,或者在尾添加一个查看更多的按钮,让用户在不滚动滚动条也能浏览列表。
App中的待办事项列表(to do list),可以给几个列表项设置大的文本样式,但随着列表项数目增加,列表项目的设置较小的字号。
用来显示颜色的调色板APP。有人可能想随着颜色数量增加,利用好空间让颜色控制面板变得更紧凑些,如下图所示:
随着颜色的数量增加,逐步缩小控制颜色的埠和减少可用空间。注意,在特殊情况之下,当只有一个颜色时,会隐藏删除按钮。
具有多个<textarea>
的APP,我们每次新增加一个文本域,他就比前一个更小一些(例如bytesizematters.com)。
然而,针对元素相邻兄弟元素来选择目标元素不是简单的CSS选择器。例如,当列表总数是4
个时给列表不同样式。我们可以使用:nth-child(4)
来选择列表的第四个列表项,但这并不是我们所需要的,我们要的是当列表总数为4
的时候,选择列表的每一个列表项。
有一个想法就是使用通用选择符(~
)和:nth-child()
选择器配合使用,例如li:nth-child(4),li:nth-child(4)~li
。不管列表项总数有多少,只选择了第四个项和其之后的所有列表项,如下图所示:
因为没有“向后选择器”和选择元素之前的兄弟元素的选择器,那么CSS就注定不能选择到需要的目标元素吗?我们不应该失去信心,一切皆有可能。
解决方案
有一个特殊案例就是只有一个单独的列项项目,有一个明显的解决方案::only-child
选择器,这个选择器就是为这种情况而生的。他是非常有用的,这也是为什么会将其添加到规范中。例如前面提到的颜色调控板APP,当只有一个颜色时,会隐藏删除按钮,那么就可以使用:only-child
选择器来实现。
li:only-child {
/* 当只有一个列表项时的样式 */
}
我们在这一节讨论的是
:nth-child()
选择器,但这一切的讨论都同样适用于:nth-of-type()
选择器。往往这种类型选择器更适用,通常情况之下,容器中会有不同类型的兄弟元素,使用这种类型选择器,我们只要关心一种类型。这里将使用列表元素做为示例讨论,但讨论中的一切都适用于任何类型元素。
然而,:only-child
相当于:first-child:last-child
,他是第一项也是最后一项,从逻辑上可以得出结论,他就是唯一的项目。其实,:last-child
就是:nth-last-child(1)
简写。
li:first-child:nth-last-child(1) {
/* 和li:only-child等同 */
}
当然,现在1
就是一个参数,我们可以根据自己的喜好调整这个参数。你能猜出li:first-child:nth-last-child(4)
选到的目标是什么?如果你回答,当列表的总数是四个的时候,选择到:only-child
的目标元素,你可能太过于乐观了一点。其实事实并不是这样,但是离我们正确的选择目标越来越接近。分别考虑两个选择器::first-child
和:nth-last-child(4)
。因此,在相同时间从元素第一个子元素开始计数和倒数第四个子元素计数。那么哪些元素满足这个范围呢?
正确答案是,只个四个元素的列表的第一个元素被选中,如下图所示:
这并不是我们想要的结果,但已非常接近我们需要的效果。因为我们在一个列表中选择到了第一个子元素,我们可以使用通用选择符(~
)来选择第一个子元素后的相邻兄弟元素。这样一来,如果列表只有四个列表项时,所有列表项都将被选中。这正是我们试图要实现的效果:
li:first-child:nth-last-child(4),
li:first-child:nth-last-child(4) ~ li {
/* 目标选择了仅有四个列表项的列表所有列表项 */
}
为了避免代码的冗长和重复的解决方案,可以使用预处理器,例如SCSS。尽管现有的预处理器语法比图笨拙:
/* 定义mixin */
@mixin n-items($n) {
&:first-child:nth-last-child(#{$n}),
&:first-child:nth-last-child(#{$n}) ~ & {
@content; }
}
/* 像这样使用 */
li {
@include n-items(4) {
/* 这里写样式 */
}
}
选择兄弟元素范围
在大多数实际运用中,我们并不希望瞄准特定数量的列表,而是希望在一个范围内。有一个小技巧,我们可以使用:nth-child()
选择器来指定一个选择范围。例如选择第四个之后的列表项。除了使用简单的数字作为参数之外,还可以使用an+b
表达式作为参数(例如::nth-child(2n+1)
),其中n
是一个变量,范围是从0
到+∞
(实际上,值在某点不会选择任何东西,因为我们元素的数量是有限的)。如果我们使用一个表达式n+b
(a
默认下是1
),那么,如果n
不是一个正整数时可以给我们一个小于b
值的范围。因此,表达式n+b
可以用来第b
个值为第一个子元素所有子元素。例如::nth-child(n+4)
选择除了第一、第二、第三的所有子元素。如下图所示:
如果你不想为
:nth-*
选择器做过多的思考,你可以使用一个测试工具,我写了一个在线的工具,足够你做测试。
我们可以利用这个功能来选择列表项是四个或更多的列表子元素,如下图所示:
在这种情况下,我们可以在:nth-last-child()
使用n+4
这个表达式:
li:first-child:nth-last-child(n+4),
li:first-child:nth-last-child(n+4) ~ li {
/* 目标列表选择最少包含四个列表项 */
}
同样,表达式-n+b
可以用来选择第b
个元素。因此,选择仅有四个或更少的列表项的列表的所有子元素时,就可以使用这种表达式。
如上图所示,我们代码可以这样写:
li:first-child:nth-last-child(-n+4),
li:first-child:nth-last-child(-n+4) ~ li {
/* 选择列表项最多只有四个的列表所有子元素 */
}
当然,我们可以结合这两个,但现在的代码变得更加笨拙。假设我们列表包含2~6
项的列表所有子元素:
li:first-child:nth-last-child(n+2):nth-last-child(-n+6),
li:first-child:nth-last-child(n+2):nth-last-child(-n+6) ~ li{
/*选择包含2-6个列表项的列表所有子元素*/
}
回过头来看本节最前面提出的案例:颜色面板APP效果:
如需转载,烦请注明出处:https://www.fedev.cn/css3/css-secrets/styling-by-sibling-count.htmljordans for sale custom