前言
最近在恶补css
的知识,看到《css揭秘》一书,如获至宝。下面节选一部分笔记。
一、扩大按钮的点击范围
对于哪些较小的、难以瞄准的控件来说,如果不能把它的视觉尺寸直接放大,将其可点击区域向外扩大也能大幅度提升用户体验。因为一个点来点去都点不到的按钮可不招人喜欢。
只要加上cursor: pointer
这个简单的css
属性,它能以视觉的方式来提示如何与之进行交互,也能试探它的交互范围有多大。
解决方案一
最简单的方法就是设置一圈透明边框,因为鼠标对元素边框的交互也会触发鼠标事件的。
border: 10px solid transparent;
上述代码就是将元素的边框扩大了10px
,但是效果并不好,因为它同时让按钮变大了。
使用border
的之所以会使按钮变大,是因为背景在默认的情况下会蔓延到边框的下层。这时使用background-clip
属性将背景限制在原来区域的下层就行。
border: 10px solid transparent;
background-clip: padding-box;
2
而且当按钮真正需要边框时,可以用box-shadow
来模拟出边框
border: 10px solid transparent;
box-shadow: 0 0 0 2px red inset;
background-clip: padding-box;
2
3
perfect
解决方案二
不用边框,而是采用伪元素来代表宿主元素来影响鼠标交互。
可以在按钮的上层覆盖一层透明的比按钮大10px
的伪元素。
btn: {
position: relative;
}
.btn::before{
content: '';
position: absolute;
top: -10px;
right: -10px;
botttom: -10px;
left: -10px;
}
2
3
4
5
6
7
8
9
10
11
这个方法的好处是只要有一个伪元素可利用,就能发挥效果,也不会干扰到其他的css
属性。
二、遮罩层
很多时候,需要通过一层半透明的遮罩层来把后面的一切遮挡住,来凸显某个特定的UI
元素,吸引用户的注意。比如弹窗等。
这个效果最常见的方案是增加一个额外的HTML
元素用于遮挡背景
.overlay{
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: rgba(0,0,0, .8);
}
.dialog{
position: absolute;
z-index: 1;
}
2
3
4
5
6
7
8
9
10
11
12
.overlay
负责把这个关键元素背后的所以东西遮挡住,.dialog
需要指定一个更高的z-index
。
虽然这个方法很好,但是需要一个额外的
HTML
元素。
解决方案一
可以使用伪元素来替代额外的HTML
元素
body.overlay::before{
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1;
background: rgba(0,0,0, .8);
}
2
3
4
5
6
7
8
9
这个方案可以直接在CSS
层达到遮罩层的效果,但还是有一些缺点:
- 如果
body
元素上已经有其他效果占用的::before
伪元素 - 往往还需要
JavaScript
给body
添加overlay
类名
解决方案二
伪元素可以满足绝大多数遮罩层的需要,但对于一些简单的应用场景来说,可以利用box-shadow
来达到这个效果。
box-shadow: 0 0 0 999px rgab(0,0,0, .8);
box-shadow
的扩张参数可以把元素的投影向各个方向延伸放大。就是生成一个巨大的投影,简单的实现遮罩层的效果。
但存在一个问题就是无法在较大的屏幕分辨率中正常工作(上述设置就无法在2000px
以上的屏幕中正常工作)。要么就加大数字来缓解,要么换成视口单位。
box-shadow: 0 0 0 50vmax rgab(0,0,0, .8);
由于投影是同时往四个方向中扩展的,所以设置为50vmax
就能满足需求。
但缺点还是很明显:
- 由于使用视口单位,当页面滚动时,遮罩层的边缘会显示出来。除非加上
fixed
定位或者没有滚动 - 当使用一个独立的元素来实现遮罩层时,这个遮罩层还可以防止用户与页面其他元素发生交互,但
box-shadow
并没有这个能力。
解决方案三
如果是想凸显一个弹窗元素<dialog\>
,而去实现一个遮罩层的话,<dialog\>
它会自带一个遮罩层。借助::backdrop
伪元素,这个原声的遮罩层也可以设置样式
dialog::backdrop{
background: rgba(0, 0 ,0, .8)
}
2
3
三、自定义下划线
尽管CSS
有一个下划线属性text-decoration: underline
,但这个属性非常简陋而且不能修改效果。
解决方案一
使用border
属性
{
border-bottom: 1px solid gray;
text-decoration: none;
}
2
3
4
使用border-bottom
模拟出来的下划线可以自定义颜色、线条宽度和线的形状。但是线与字之间的距离或者说空隙很大
如果设置比较小的line-height
,比如line-height: .9
,距离的确能缩小,但又产生另一个问题:阻止正常的文本换行。
解决方案二
使用background-image
属性
background: linear-gradient(gray, gray) no-repeat;
background-size: 100% 1px;
background-position: 0 1.115em;
text-shadow: .05em 0 white, -0.05em 0 white;
2
3
4
效果非常棒。也可以实现不同的线条类型:比如虚线下划线
background: linear-gradient(90deg, gray 66%, transparent 0) repeat-x;
background-size: .2em 2px;
backgrond-position: 0 1em;
2
3
通过色标的百分比位置值来微调虚线的虚实比例,还可以通过background-size来改变虚线的疏密。
四、自适应内部元素
如果不给元素一个具体的height
,它会自动适应其内容的高度。那width
能不能设置成这样的行为呢?
比如,如下html
代码
<p>Some text</p>
<figure>
<img src='adacatlace.jgp'>
<figcaption>
The great Sir Adam Catlace was named after
Countess Ada Lovelace, the first programmer
</figcaption>
</figure>
2
3
4
5
6
7
8
在默认情况下,如上图所示,但系需求是这个
figure
元素能跟它包含的图片一样宽(尺寸往往不是固定的)而且是水平居中的。
比如说让figure
元素浮动起来,这样就会得到正确的宽度,但这种方法的副作用非常明显,将布局模式都改变了。
解决方案
使用min-content
属性
figure{
width: min-content;
margin: auto;
}
2
3
4
两行css
代码就能完成。
min-content
关键字将解析为这个容器内部最大的不可断元素的宽度————最宽的单词、图片或具有固定宽度的盒元素。
五、紧贴底部的页脚
具有块级样式的页脚,怎么让它紧贴内容的下方呢?
假设页面的HTML
结构如下:
<header>
<h1></h1>
</header>
<main>
...
</main>
<footer>
...
</footer>
2
3
4
5
6
7
8
9
解决方法
使用flexbox
布局,首先对body
元素设置display: flex
,因为它是这三个元素的父元素,还需要设置flex-flow: column
,否则子元素会被水平的排放在一行上。设置高度为100vh
占满整个视口的高度。
页头和页脚的高度是由main
来决定的,所有给main
的flex
属性指定一个大于0
的值。
body{
height: 100vh;
display: flex;
flex-flow: column;
}
main{
flex: 1;
}
2
3
4
5
6
7
8
六、根据兄弟元素的数量来设置样式
在某些场景下,需要根据兄弟元素的总数来为它们设置样式。最常见的场景就是,当一个列表不断延长的时候,通过隐藏控件或压缩控件等方式来节省屏幕空间,以此提升用户体验。
解决方案
1、只有一个列表项的特殊场景来说,使用:only-child
伪类选择器。
当列表只有一个列表项时,把删除按钮隐藏起来,就需要:only-child
选择器来完成
li:only-child{
background: red;
}
2
3
:only-chil
d等效于:first-child:last-child
,因为第一项同时也是最后一项,那么它就是唯一的。
:last-child
也是一个语法糖,它等价于:nth-last-child(1)
。
2、列表正好包含四个列表项时,命中它的每一项
li:first-child:nth-last-child(4),
li:first-child:nth-last-child(4) ~ li{
background: red;
}
2
3
4
当然拉,这部分代码还是十分繁琐的,可以利用
scss
预处理器来避免这个问题
@mixin n-item($n) {
&:first-child:nth-last-child(#{$n}),
&:first-child:nth-last-child(#{$n}) ~ & {
@content
}
}
li {
@include n-item(4) {
// 具体样式写这里
}
}
2
3
4
5
6
7
8
9
10
11
12
3、列表项的总数是4或者更多时,选中所有的列表项
li:first-child:nth-last-child(n+4),
li:first-child:nth-last-child(n+4) ~ li{
background: gray;
}
2
3
4
少于4个li
元素时:
多于4个li
元素时:
4、同理,仅当列表中有4个或更少时,选中所有列表项
li:first-child:nth-last-child(-n+4),
li:first-child:nth-last-child(-n+4) ~ li{
background: yellow;
}
2
3
4
5、列表包含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{
background: #456789;
}
2
3
4
大于6个:
大于等于2个,少于等于6个:
少于2个:
参考:
《css揭秘》